Raku / problem-solving

🦋 Problem Solving, a repo for handling problems that require review, deliberation and possibly debate
Artistic License 2.0
70 stars 16 forks source link

Semantics of coercion type on an "rw" parameter #21

Open jnthn opened 5 years ago

jnthn commented 5 years ago

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.

jnthn commented 5 years ago

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.

Xliff commented 5 years ago

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?

alabamenhu commented 5 years ago
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.

jnthn commented 5 years ago

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

raiph commented 3 years ago

Is there consensus that it would be good to have a general principle along the following lines?

patrickbkr commented 3 years ago

@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?

niner commented 3 years ago

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

lizmat commented 3 years ago

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