Calling and coercion kinda share syntax and the boundaries aren't as clear as would be desirable. Also we didn't quite settle on coercion stuff yet, and there are some things we need to decide whether to keep.
Done by writing a CALL-ME
method, which gets the args passed (not the Capture, but directly the
args). All previous forms are gone (so no more method postcircumfix:<( )>
).
We currently have:
method &.(|args) { } # mapped to postcircumfix:<( )>
method @.(|args) { } # mapped to postcircumfix:<[ ]>
method %.(|args) { } # mapped to postcircumfix:<{ }>
These are useless. We should kill them off, or change them to:
method &.(|args) { } # mapped to CALL-ME
method @.($idx) { } # mapped to AT-POS
method %.($key) { } # mapped to AT-KEY
They may not be worth keeping. The last two are a tad odd as they special-case just one of the things
you should implement if making a custom indexable type (you really want to do EXISTS-POS
as well as
AT-POS
, for example).
Anything that matches:
<typename> '(' <.ws> ')'
<typename> '(' <typename> ')'
Is a coercion type literal. Example: Int(Str)
. Open question: do we need a (non-MOP) way to make a
coercion type out of two other types? (You can always do it via. the MOP if needed). This means that
MyType(SomeType)
will never call a CALL-ME
in MyType
.
Coercions are a bit of a mess at the moment. They're very confused with invocation - and the way
to implement a coercion seems to be "implement CALL-ME
", which isn't really intuitive. We also
have no syntactic distinction for what is/isn't a coercion. Here's a proposal.
Anything that matches:
<typename> '(' <EXPR> ')'
Will be taken as a coercion. Note that we can't quite implement it like this, since in the grammar
this is spread over term:sym and term:sym. So both of those need to look if the
thing they parsed is a type. Assuming the parsed type is $type
, $typename
contains the compile
time evaluated result of $type.^name
, and the parsed expression is in $expr
, a coercion will
compile to the following:
nqp::istype($expr, $type)
?? $expr # Already matching type
!! nqp::can($expr, $typename)
?? $expr."$typename"() # Coercion method
!! $type.COERCE($expr) # Call .COERCE
This is the code that a coercion in a signature should also result in. Notice that in the no
coercion case a dynamic optimizer (like MoarVM spesh) can maybe toss the whole lot and just
reduce it to $expr
, and in the csae we need a coercion it can often resolve the nqp::can
at specialization time too.
Note that $foo(42)
is always unambiguously a call (and goes to CALL-ME
). To force a
call on a type object, do MyType.(42)
(the extra dot forces it to be invocation, since
it doesn't syntactically get considered a coercion).
The default COERCE
method dies with an error saying it doesn't know how to coerce from
one type to the other, which is a rather better failure mode than "no such method X" or
"no CALL-ME" and so forth.
Open question: how do we provide a nice way to get access to the coercion logic (check type, then check for method, then call COERCE) when you don't want to write the type literally into the code? Preferably as a compiler-recognized form so we can do the same nice code-gen and give dynamic optimizers a chance at doing something sane in the non-megamorphic case.
One thing this gist does not cover, are the method forms for coercions.
This too is a bit of a mess currently in Rakudo, with method and "sub" forms being synonyms for each other for some built-in types, but not others:
I think TimToady once recommended on IRC that one of those forms should automatically fall back to the other, but I don't remember which, and can't seem to find the quote on the quick.
In any case, it would be nice to have consistency there (for both built-in types and user types).