problem-solving
problem-solving copied to clipboard
Semantics of coercion type on an "rw" parameter
Currently, if writing:
sub foo(Num() $n is rw) { $n++ }
Then we can call it successfully like this:
my $x = 1e0;
foo($x);
dd $x; # Num $x = 2e0
However, if the coercion is applied, such as in this case:
my $x = 1;
foo($x);
Then it binds the result of the coercion to $n
in foo
, resulting in an error since ++
is being done on an immutable value. This is almost certainly the result of not having considered how this interaction should work.
Initial proposal: I see two main options.
We could make it a compile-time error, so folks can't run into it accidentally. This avoids any further gotchas in this area. However, this could break working code that is successful because all the cases that exercise it pass a matching type.
Alternatively, we could define the use of a coercion type together with is rw
as meaning that we should assign the result of the coercion into the passed container. This would mean that:
sub nummy(Num() $x is rw) { }
my $x = 42;
nummy($x);
Would result in $x
being coerced an Int
after the call. That might be OK, however there's a problem with multi-dispatch that needs a bind check. For example:
multi sub nummy(Num() $x is rw, Num $y where something-needing-nums($x, $y)) { }
Would require one, and would potentially as part of signature processing mutate $x
- even in the case that the multi
candidate were not selected because the where
clause failed to match! It's not like you can't construct such cases today anyway, by doing side-effects in a default expression or a where
clause. But it's worth considering whether that I managed to think of a surprise within 5 minutes consideration might mean there's others we didn't think of, if we try to make this DWIM.
In this situation, I suggest that in this situation:
sub nummy(Num() $x is rw) { }
my $x = 42;
nummy($x);
The coercion is fine, since there is no type applied to $x. However in the more restrictive situation:
sub nummy(Num() $x is rw) { }
my Int $x = 42;
nummy($x);
The coercion is incorrect and should generate a run-time exception.
Thoughts?
sub nummy(Num() $x is rw) { } my Int $x = 42; nummy($x);
The coercion is incorrect and should generate a run-time exception.
Thoughts?
I don't mind that behavior at all. Just a quick type check on the assignment.
my Cool $x = 42;
my Str $y = "42";
nummy($x); #Â no error
nummy($y); # error
I think that'd be a pretty nice if eventually we could specify coercion on assignment such that:
my Int() $x = 42;
nummy($x);
Could recoerce back to Int upon completion, if possible, and generate a run-time exception otherwise, but doing a coercing assignment would be a very different topic indeed, so I digress.
As to the concerns that Jonathan had, from a user perspective, I'd expect
multi sub nummy(Num() $x is rw, Num $y where something-needing-nums($x, $y)) { }
to work more or less like this:
sub nummy($x is rw, Num $y) {
my Num $x-temp = $x.Num;
die unless something-needing-nums($x, $y);
# signature match is successful so …
$x = $x-temp;
# begin nummy
...
}
In other words, the coerced value would be temporarily stored, and only properly assigned to the rw container upon a successful signature completion.
... The coercion is incorrect and should generate a run-time exception.
This is what I'd expect, yes.
In other words, the coerced value would be temporarily stored, and only properly assigned to the rw container upon a successful signature completion.
Signature binding is defined as processing the parameters in order. The point of the where
clause in the example is that we actually expect $x
to be the coerced value at that point, which it would not be with the proposed desugar. As another example, consider (Num() $x is rw, Num $y = $x)
, which would clearly fail if $x
did not contain the Num
by the point the default of $y
was evaluated. I'm not convinced there's any trickery we can do with regards to delaying it that I can't come up with a counter-example for.
Is there consensus that it would be good to have a general principle along the following lines?
-
If a problematic semantic is being discussed; and
-
The option to disallow the problem by newly making it a compile time failure is available; and
-
Discussion stalls before a consensus is reached on what, who, and when a better solution will be introduced (and perhaps even then);; then
-
The compile-time rejection should be considered for introduction on master as a probably appropriate immediate step; with blining to see what fall out there appears to be, and then introduction into a subsequent release, to stop the problem of code relying on the given problematic semantic becoming more entrenched.
@raiph I do like the idea to introduce compiler errors when a construct is clearly problematic and serves no use. How could such a policy introduced? Is this a topic for the RSC?
I feel like this problem is too abstract to have a good discussion about it. Best answer I could give is probably "sounds sensible, but the best course of action depends much more on the concrete semantic".
As far as I can remember, making something a compile time error when semantics are still unclear, has been the policy. That's why we have a X::NYI
exception :-)