Search code examples
rakumeasureunits-of-measurementrakudo

How to apply one signature test to multiple positionals


I wrote some code in https://github.com/p6steve/raku-Physics-Measure that looks for a Measure type in each maths operation and hands off the work to non-standard methods that adjust Unit and Error aspects alongside returning the new value:

multi infix:<+> ( Measure:D $left, Real:D $right ) is export {
    my $result   = $left.clone;
    my $argument = $right;
    return $result.add-const( $argument );
}
multi infix:<+> ( Real:D $left, Measure:D $right ) is export {
    my $result   = $right.clone;
    my $argument = $left;
    return $result.add-const( $argument );
}
multi infix:<+> ( Measure:D $left, Measure:D $right ) is export {
    my ( $result, $argument ) = infix-prep( $left, $right );
    return $result.add( $argument );
}

This pattern is repeated 4 times for <[+-*/]> so it amounts to quite a lot of boilerplate; I'd like to reduce that a bit.

So, is there a more terse way to apply a single Measure|Real test in the signature to both Positionals in a way that the multi is triggered if both or one but not neither match and that the position is preserved for the intransigent operations <[-/]>?

I am not sure that getting to no multis is the most elegant - perhaps just compress the Real-Measure and Measure-Real to one?


Solution

  • There are a few ways to approach this but what I'd probably do – and a generally useful pattern – is to use a subset to create a slightly over-inclusive multi and then redispatch the case you shouldn't have included. For the example you provided, that might look a bit like:

    subset RealOrMeasure where Real | Measure;
    multi infix:<+> ( RealOrMeasure:D $left, RealOrMeasure:D $right )  {
        given $left, $right {
           when Real,    Real    { nextsame }
           when Real,    Measure { $right.clone.add-const($left)  }
           when Measure, Real    {  $left.clone.add-const($right) }
           when Measure, Measure { my ($result, $argument) = infix-prep $left, $right;
                                   $result.add($argument)}}
    
    }
    

    (Note: I haven't tested this code with Measure; let me know if it doesn't work. But the general idea should be workable.)