Apologies to those that have OO steeped in their blood. I am a wary traveller in OO space, maybe I am an technician, not an architect at heart. So for me, no sweeping frameworks unless and until they are needed. And, frankly, one can go a long way on procedural code with subroutines to gather repetitive code sequences.
(And don’t get me started on functional programming…)
Some time ago, I was tasked to write a LIMS in old perl. Physical ‘aliquots’ with injections of various liquids would be combined and recombined by bio-machines. This led to a dawning realization that these aliquot objects could be modelled in an object style with parent / child relationships. After a few weeks, I proudly delivered my lowly attempt at ‘Mu’ for this (and only this) problem. Kudos to the P6 team – after a couple of weeks in here, it’s just sensational the level of OO power that the real Mu delivers:
Now, hard at work, on the perl6 version of Physics::Unit, I am wondering how to put the OO theory into productive practice. One of my aims was to find a medium sized (not core tools) problem that (i) I know something about and (ii) would be a good-sized problem to wrangle.
So I am enjoying the chance to design some classes and some interfaces that will make this all hang together. But – as an explorer, it has become clear that I only have three options. The problem runs like this:
- There is a parent Measure class that contains the methods for any real-world measurement handling dimension, units, value and errors.
- Then there are child classes for my Distance $d = ’42 m’, my Time $t = 3 s’, etc.
- First level, I have an operation like … my $r = ’23 ft’ + $d;
Initially I had some success with object types ::T – but these only let you read the type and duplicate if needed for a new left hand side container. Then I tried the built in (shallow) clone method. But…
- What about the operation … my $s = $d / $t?
- How do I create a new Speed object programatically?
Ultimately, thanks to rosettacode.org, I worked out that $x.perl.EVAL with some ~~ s/// substitions on the way would do the trick!
Phew. Please comment below if you have a better way to share – or would like to point out the risks of this technique…
So I’m on the train at the moment so can’t easily check the docs but I am pretty sure you can make a new multi infix \ operator that matches a Distance on the left and Time on the right and returns a Speed.
When I get into the office and there are less tunnels I shall try and find some more helpful detail.
LikeLike
Thanks Simon – that would be much appreciated. I am using an infix to do “Measure Maths” – such as Speed = Distance / Time to handle units and errors. When it comes to the types, these all inherit from a general Measure type. I need to generate a new child at runtime from a string – e.g. my Str $s = ‘Speed’; my ::T = $s; my ::T .=new (value => 42, units => m/s); My understanding of perl6 type variables is that I can load ::T from the infix or method signature if I have that type passed in, but I cannot interpolate a string into it to dynamically set the child Type.
LikeLike
In Perl 6 types are objects. To create an instance at runtime you need a type literal or a variable that hold the type. To get a type object form a name of a type at runtime, use the ::() dynamic lookup operator.
my $type = ‘Int’;
my $type-object = ::($type);
my $instance = $type-object.new(10);
dd $instance;
#OUTPUT: «Int $instance = 10»
LikeLike
That’s the ticket – thanks!
LikeLike
So here’s a very quick version of my idea. We define a Unit role and then differnt unit types. Then we can add a new infix / that is a Speed constructor.
—-
module SI {
role Unit {
has Numeric $.size;
has Str $.descriptor;
method Str(–> Str) {
return “{$.size}{$.descriptor}”;
}
method Rat(–> Rat) {
return $.size.Rat();
}
}
class Distance does SI::Unit {
submethod BUILD( Numeric :$size ) {
$!size = $size;
$!descriptor = ‘m’;
}
}
class Time does SI::Unit {
submethod BUILD( Numeric :$size ) {
$!size = $size;
$!descriptor = ‘s’;
}
}
class Speed does SI::Unit {
submethod BUILD( Numeric :$size ) {
$!size = $size;
$!descriptor = ‘m/s’;
}
}
multi infix:( SI::Distance $d, SI::Time $t –> SI::Speed ) is export {
return SI::Speed.new( size => $d.size / $t.size );
}
}
LikeLike
One could improve https://gist.github.com/5ff7b47eb10ebeff0c4913e38259918b by having a proper regexp to take the unit part of the matcher from %suffix-to-type. Also a slang would be better for literals then loads of operators. (Re-)Defining operators is still really slow in Rakudo.
But not me, because I need to rush to work!
LikeLike
Nice work! Wish I could be that fast!
LikeLike
I think the amount of user variation expected on inputs – everything from miles per hour through m/sec^2 could use a Grammar. Gives me a chance to try that too! Since only +-*/ make sense I have been planning to overload these in a visible way… need to think about a slang.
LikeLike
sub postfix:( Numeric $size –> SI::Distance ) is export {
return SI::Distance.new( size => $size );
}
Lets you do :
my $d = 10m;
Which is nice too.
LikeLike
Thanks scimon – I think that this combined with the ability to dynamically pick the type from a string with ::($type-name) is ideal…
LikeLike