11

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?

TylerH
  • 20,799
  • 66
  • 75
  • 101
librasteve
  • 6,832
  • 8
  • 30
  • .oO ( ["In the process of automating bakery operations, one of the most difficult and intransigent operations has been that of loading the transport racks"](https://www.google.com/search?q=%22intransigent+operations%22) ) – raiph Dec 30 '21 at 17:18
  • [hammer of intransigence](https://www.discogs.com/master/416604-Heresiarch-Hammer-Of-Intransigence) – librasteve Jan 22 '22 at 20:32
  • Ahh. .oO( "hammer that down" = "opinion-based language"! ) – raiph Jan 23 '22 at 17:26

3 Answers3

10

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.)

codesections
  • 8,900
  • 16
  • 50
  • 2
    `subset RealOrMeasure where * ~~ Real | Measure;` Doesn't just `subset RealOrMeasure where Real | Measure;` work? – raiph Dec 30 '21 at 01:11
  • 1
    @codesections / @raiph -- yes that's exactly what I had in mind thank you! One thing this raises is if there is a way to type check all Positionals in one go, something like ```( ($left, $right).all ~~ RealOrMeasure:D )``` – librasteve Dec 30 '21 at 10:16
  • 4
    Note that subset types are tie-breakers, so using this approach will impact upon the way the candidates are sorted, which might lead to surprises. – Jonathan Worthington Dec 30 '21 at 22:17
  • ( Wait till ... Merry 2022! ) @JonathanWorthington I'm not sure what surprises you mean, and can't tell what specifics of "this approach" risk incurring surprises. Perhaps the surprises I avoided in my answer (by introducing a `proto`) are (some of?) the surprises you're talking about? And/or perhaps you know of / can link to an existing resource (eg a doc section or SO) that digs into what you're talking about? And/or is [this](https://design.raku.org/S12.html#Multisub_Resolution) appropriate food for thought? And/or perhaps you could write a new SO Q+A pair that outlines the surprises? – raiph Dec 31 '21 at 20:26
  • 3
    @raiph For example, compare `multi m(Int) { 1 }; multi m(Real) { 2 }; say m 1` (nominal type match, `Int` is tighter) against `subset IntSub where Int; multi m(IntSub) { 1 }; multi m(Real) { 2 }; say m 1` (the `subset` implies a nominal type of `Any`, and `Real` is tighter). The `proto` approach may well avoid that, but implies cost of redispatch and won't compose if multiple modules attempt that strategy. – Jonathan Worthington Jan 01 '22 at 23:14
3

You can add an Int - method to your class and use signature coercion.

class Foo { 
    method Int { 110 };
} 
multi t( Int() $x ) { $x + 1 };
multi t( Real $x ) { $x.Int + 11 };

say t( 0 );
say t( 0.1 );
say t( Foo.new );
Holli
  • 5,072
  • 10
  • 27
  • 5,000 is better than 5,002 –  Dec 31 '21 at 19:45
  • @holli - thanks for your answer, but I need to preserve the Real-ness of any values... for example ```$x = Measure.new(value => 27.5, units => 'm') + 13.2; say +$x; #40.7``` – librasteve Dec 31 '21 at 21:48
0

I just found this example in the raku docs:

sub f(*@a where {$_.all ~~ Int}) { say @a };

I now plan to combine this with @codesections answer...

librasteve
  • 6,832
  • 8
  • 30