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

Reply via email to