On Wed, 17 Aug 2016 09:03:15 -0700, c...@zoffix.com wrote: > Tests added in > https://github.com/perl6/roast/commit/d0f819872953d08cd9732fef50f2567264fcf41b
There is still the matter of Numeric() in a signature. However since: $ perl6 -e 'my Numeric() $a;' ===SORRY!=== Error while compiling -e Coercion Numeric(Any) is insufficiently type-like to qualify a variable at -e:1 ------> my Numeric() $a⏏; expecting any of: constraint ... one wonders what the fate of Numeric() is. WRT to the subject of what .Numeric/.Real/.Int/etc do (long screed follows): Here's how I construe the reasoning for the decision to have True.Numeric/True.Real/True.Int behave as they now do: Casts to the Numeric/Real *roles* are used in the arithmetic core as a way to limit the number of candidates needed for ops. So .Numeric is sort of a first stage to get classes that can numify into any of Int/Num/Rat/Complex arithmetic, and then later if broadening is needed, .Bridge is used to move Int and Rat into Num arithmetic (though theoretically, Int should move up through Rat and FatRat before getting to Num... possibly it does in some cases.) Trying to fit type calculus onto the arithmetic system is fraught with challenges: Binary numeric operations are supposed to produce the broader of the involved types. However, in the few places where there is a class/subclass relationship between numeric types (e.g. int isa Int) it is the narrower type which is the subclass. Meanwhile, enhancements to types are also subclasses (e.g. IntStr isa Int). Enums, horribly, happen fall into both categories: they are both a subset of Int and an enhancement to carry identifier keys alongside a value. Finally you have use cases both for defining narrower numeric types and for defining broader cases. If you want an "MyInt is Int" with a .even method, there's a good chance you are going to want to be able to add 1 to it and get an IntEven. But if you have a "subset of Int" you probably want to be able to produce results from outside that subset... you only want the things to which you specifically apply that type to be subject to those constraints. The current system is only good for the latter, and allows you to swim with the class system in that case and use MMD productively during that process. For classes that do not have the Numeric role, the .Numeric method allows them to shed their class when entering the arithmetic regime. For classes that do have the .Numeric role already, the method would be useless/ancillary... they should never hit .Numeric calls in the arithmetic core as they'll directly hit candidates of Numeric or more specific types. In the case of enums, where you have extra data attached, it's reasonable for the .Numeric method to do essentially what it does for non-Numeric classes... ditch your subclass and become an instance of another (in this case, a parent) class. The current behavior allows enum and IntStr appear to behave the same as a numifiable non-Numeric class, which is probably the most common kind of class a user would implement, and hence the behavior most often expected from .Numeric. Now, a good case might be made that really Perl6 should have named .Numeric something like .Numify so there was no expectation for class-like coercive no-ops, but I think that horse has left the barn. This is more grating to FP adherents in the case of .Int/.Rat/etc which are classes rather than roles, but in this case there is also the additional practical use case of how one should go about releasing a value from the constraints of a subset type when that is what you want to do. Not that no-ops are never a useful behavior, but they are not as useful as that. BTW, I'd argue this should also work here where it doesn't, currently: my $a = 5 but "foo"; $a.Numeric.Str.say; # Should probably say "5" Something in the future needs to be done for the former case a few paragraphs up, where you want a sticky enhancement to a base Numeric class. In this case, if you want to avoid writing a full set of arithmetic candidates and do not care what type interim calculations are performed on, some role defined in core or module space that provides candidates that .Numeric, perform the op, then cast (or clone with tweaks) final results back to the broader operand's class might be sufficient (along with some system to decide what is broader). For more complex things like logging/Proxy classes where interim results need to be in your class, a prefab solution might have to wait for macros. Efficiency concerns will come up with any of that. I think the idea of an "exact type smiley" that prevents subclass matches (without using an optimizer/mmd unfriendly where clause) might help here as well. In addition, we could define a ".Int:E" method (or whatever character the smiley ended up being), etc to do what the current behavior does, which could be used to alleviate type calculus guilt, since it would pretty explicitly be saying "coerce into this exact type, don't care if you are already a subclass."