New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nil ignores default value in parameter list #1245

Closed
gfldex opened this Issue Nov 10, 2017 · 4 comments

Comments

Projects
None yet
3 participants
@gfldex
Contributor

gfldex commented Nov 10, 2017

Currently Nil is assigned to the parameter even though a default value is assigned.

sub f($a = 42){ say $a }; f Nil;
# rakudo-moar 17db03eb3: OUTPUT: «Nil␤»

This is at best surprising and may lead to undefined behaviour of a program as a default value is meant to replace and as such avoid undefined values in the routine. Using the default value when Nil is assinged would avoid that problem.

One complication would be a is raw parameter that got a default value (what in itself may be problematic). As is raw is meant to pass whatever is provided as an argument to the routine. In this case Nil should be passed on even if there is a default value.

see discussion at https://irclog.perlgeek.de/perl6/2017-11-10#i_15429754

@zoffixznet zoffixznet added the RFC label Nov 10, 2017

@zoffixznet

This comment has been minimized.

Contributor

zoffixznet commented Nov 10, 2017

Feels like a conflation of parameter defaults with container defaults. This sub f($a is copy = 42){ say $a = Nil }; f Nil; prints (Any), not 42 because the default is for the parameter, not the container. If we make the Nil passed to a sub mean the parameter's default is used, would that also become the container's default? Also, how would you pass a Nil literally, if you wanted to? Also, what happens when there is no default value, would Nil become an Any?

Another issue crops up: what would this dispatch to:

    multi x     { say "here" }
    multi x ($) { say "there" }
    x Nil

If Nil is meant to have same semantics as if no value were passed, then the first multi would be it.

Perhaps it'd be better to make is default trait work on parameters and then users can choose the behaviour they want.


It is a bit weird tho that by merely assigning the variable to itself, without any other operations, you can change its value:

    sub f($a is copy = 42) {
        say $a;
        say $a = $a
    }
    f Nil;
    
    # OUTPUT:
    # Nil
    # (Any)
@gfldex

This comment has been minimized.

Contributor

gfldex commented Nov 10, 2017

A proposed solution is to allow //= in a Signature.

sub foo($bar //= "hi") {  say $bar };
foo Nil;
# OUTPUT:
# hi
@jnthn

This comment has been minimized.

Member

jnthn commented Nov 10, 2017

Currently Nil is assigned to the parameter even though a default value is assigned.

Parameters are not assigned to, they are bound to, thus "signature binding". The parameter default is also bound. The resetting behavior of Nil is a property of assignment, by contrast.

Another difference is that the is default(...) of a Scalar is a compile-time constant value, while a parameter default is a bit of code that is evaluated every call to produce a value. Effectively, it's a branch on whether the argument was passed - which is a useful behavior in itself.

While we use the word "default" for both, they also look different (is default(...) vs =) from a syntactic point of view, which does fit the "different things look different" test - somewhat. Admittedly, we overload = to mean more than just assignment in declarative contexts (sigilless vars, constants, etc.)

Another consideration is this:

perl6-m -e 'sub foo(Int $x = 42) { }; foo(Nil)'
Type check failed in binding to parameter '$x'; expected Int but got Nil (Nil)
  in sub foo at -e line 1
  in block <unit> at -e line 1

Meaning the feature would be useless in the case of a type constraint on a parameter. I fear this would then lead to a push to make Nil special for parameter type-checking purposes, and that way lies pain.

A change to make Nil special, aside from language design considerations, also carries implementation issues. Defaults are something that we've figured out how to optimize pretty well these days. Adding extra semantics atop of the current ones will increase the generated code size of every use of a default, and complicate the control flow for processing them, and so would certainly need a careful look at whether we'd be hindering optimizability.

This is at best surprising

Given I implemented both kinds of default, I'm about the worst person to comment on whether it's surprising, because of course it isn't to me. :-) That said, given the two look different in code anyway, the only thing I can see that relates them is the use of the word "default".

may lead to undefined behaviour of a program as a default value is meant to replace and as such avoid undefined values in the routine

Could one not make the very same argument about a Failure or a type object being passed also, though?

sub f($a is copy = 42) {

This is an interesting case because here there is a Scalar container, and one would perhaps expect the assignment of the Nil into it to have assignment semantics (thus making it Any). It's tempting to consider the current behavior a bug.

A proposed solution is to allow //= in a Signature.

I already saw somebody note that this could cause Failures to vanish all too easily, since those too are undefined. I hadn't considered that originally, and now it's been pointed out it concerns me too much to back that approach any more.

@zoffixznet

This comment has been minimized.

Contributor

zoffixznet commented Jun 28, 2018

Parameters are not assigned to, they are bound to, thus "signature binding". The parameter default is also bound. The resetting behavior of Nil is a property of assignment, by contrast.
[...]
I hadn't considered that originally, and now it's been pointed out it concerns me too much to back that approach any more.

Based on the discussion above, I'm closing this ticket.

@zoffixznet zoffixznet closed this Jun 28, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment