Skip to content
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

The undefinedness of optional arguments and typed attributes in Rakudo #929

Closed
p6rt opened this issue Apr 21, 2009 · 5 comments
Closed

The undefinedness of optional arguments and typed attributes in Rakudo #929

p6rt opened this issue Apr 21, 2009 · 5 comments

Comments

@p6rt
Copy link

p6rt commented Apr 21, 2009

Migrated from rt.perl.org#64928 (status was 'resolved')

Searchable as RT64928$

@p6rt
Copy link
Author

p6rt commented Apr 21, 2009

From @masak

Apologies, the below discussion is long and involved. I've tried to
simplify and reduce as much as I can.

<masak> rakudo​: class A { multi method dispatch(@​chunks, %param?) {
say %param.perl, defined %param } }; A.new.dispatch([1,2,3]);
A.new.dispatch([1,2,3], { "OH" => "HAI" })
<p6eval> rakudo 69b318​: OUTPUT«{}1␤{"OH" => "HAI"}1␤»
<masak> I'm slightly surprised that the former is defined.
<masak> but maybe it has to be that way.
<moritz_> masak​: that's because %param is an instance
<masak> an instance?
<moritz_> objects
<jnthn> An instance of Hash
<moritz_> objects of normal classes are always defined
<masak> ok, you two don't seem to think this is an error, so I guess it isn't :)
<frettled> masak​: I think I grokked the code in your previous example
now, and yes, think it makes sense that it works the way it did.
<masak> frettled​: with the instantiated hash?
<frettled> masak​: yep
<masak> frettled​: well, I just want a simple way to test if the
parameter was passed.
<masak> frettled​: or maybe {} is the default for Hash...
<frettled> masak​: the way I read the spec, the default values are the
ones you specify in the sub declaration.
<moritz_> frettled​: if %h were undef, you couldn't assign values to it
<masak> ack. it's more dwimmy that it's {}.
<frettled> moritz_​: hrm, that's breaking the principle of least surprise.
<masak> but... I just want a nice way to see if the hash was passed or not.
<moritz_> frettled​: maybe the spec needs to be updated to only refer
to scalar optional parameters
<frettled> moritz_​: or to allow assignment to undefined non-scalars ;)
<p6eval> rakudo 69b318​: OUTPUT«{"a" => 5, "b" => 2, "c" => 3}␤»
<moritz_> frettled​: that would be much weirder, overall
<frettled> moritz_​: would it?
<moritz_> frettled​: yes.
<frettled> moritz_​: I don't see how, though.
<moritz_> frettled​: if you have an undef value, it's usually not a
"normal" object, but a proto object
<moritz_> frettled​: so if a default value is such a proto object, a
%h<a> = 3 would call the postcircumfix​:<< < > >> method as a class
method
<moritz_> frettled​: which doesn't have access to attributes - so it's
going to barf
<masak> moritz_​: what does 'my %h;' do?
<moritz_> masak​: it calls Hash.new under the hood, I assume
<masak> then it can be argued that '%h?' should, too.
<frettled> moritz_​: so essentially, it's been designed into a corner
where you can't get out of without getting hacks all over your feet ;)
<moritz_> frettled​: right
<frettled> moritz_​: $expletive
<moritz_> frettled​: of course there are other options...
<frettled> moritz_​: yes, _is rw_, for instance, seems like it would help.
* frettled is a Perl 6 spec n00b, though.
<moritz_> frettled​: like having %h? defaulting to a Hash.new(defined => 0) or so
<frettled> moritz_​: no, that's the wrong solution, because it still
means you can't say whether you got passed an empty hash or not.
<moritz_> frettled​: you could, because an empty hash defaults to being defined
<frettled> ah, I misunderstood the magic
<moritz_> but there's a drawback... every modifying call to a hash
would have to set its defined flag
<frettled> mm
<frettled> I think this is a design weakness.
<moritz_> the problem is that we're doing in-band communication
<moritz_> instead of asking the hash parameter if it was defined, we
should ask the capture if that hash was given
<masak> aye.
<moritz_> especially since objects can respond with False to .defined
at will anyway
<frettled> the way you put it, it seems like there is a
straight-forward way of doing so, and that would be good.
<frettled> Conceptually, that's much cleaner than asking the variable.
<moritz_> the simplest solution that works with the current spec is to
split it into two multis
<moritz_> but that's not always convenient
<frettled> I was considering to suggest that, but I thought it would
be neater to use the optional argument as, well, optional.
<frettled> In more complex cases it would make more sense to create
multiple methods.
<frettled> (going Java)
<frettled> There doesn't appear to be a test for this in S06, though.
Unless I'm misreading the tests or missing a test, only scalars are
tested. But my test-fu is weak.
<moritz_> frettled​: it might well be missing... but let's try to get
clarification on the spec first, and then write tests :-)
<frettled> moritz_​: yep.
<frettled> moritz_​: though the spec is pretty clear about it, really,
I just had to read the right part of it.
<frettled> It's right there in the intro of S06/Parameters and arguments/
<frettled> It speaks of "container argument" as opposed to a scalar.
<frettled> so if anyone has the test-fu to write a test for testing
whether optional hash and array parameters are 1) immutable, 2)
defined with their correct default value, and 3) handles correctly
with is rw, is copy, etc.
<frettled> ...that would be nice. Then I can leech on and see how
that's tested. :D

...meanwhile, a bit later...

<masak> rakudo​: class A { has Callable $.c; method foo() { say defined
$.c } }; A.new.foo
<p6eval> rakudo 69b318​: OUTPUT«1␤»
<masak> I must say this is highly counterintuitive.
<masak> if that one is defined, what in the world is it defined to be?
<masak> I feel both this case and the case with the default parameters
undermines thewhole concept of definedness.
<masak> to me, 'defined' should be able to show whether the variable
in question has been assigned to, initialized etc.
<masak> moreover...
<masak> rakudo​: class A { has $.c; method foo() { say defined $.c } };
A.new.foo[14​:28]
<p6eval> rakudo 69b318​: OUTPUT«0␤»
<masak> now it works as we all expect it to!
<masak> so the current behaviour is actually a kind of punishment for
adding types!
<baest> masak​: I think it's only Roles
<baest> rakudo​: class B { }; class A { has B $.c; method foo() { say
defined $.c;} }; A.new.foo;
<p6eval> rakudo 69b318​: OUTPUT«0␤»
<masak> baest​: I think you're right.
<masak> but I don't think roles should behave like this either.
<baest> indeed
<baest> would be _very_ weird
<masak> moritz_ seemed to argue earlier that the optional-params case
was correct.
<frettled> Yes, at least correct according to spec, and as far as I
could tell, it was.
<masak> right.
<masak> that's why I'm not submitting it as a rakudobug, because it's
really a spec issue.
<frettled> masak​: Yes, and I don't think I quite got an answer to how
to check whether an argument was used in any other way than doing
multiple dispatch to eliminate the alternatives.
<masak> frettled​: indeed.
<masak> this discussion is not-yet-resolved.
<frettled> Since defined() obviously isn't the correct way to do it,
an alternative method is necessary, e.g. another property for each
parameter that's specified in the sub/method definition.
<frettled> That would be quite useful, since it allows us to separate
between the following cases​:
<frettled> 1) The parameter was not used.
<frettled> 2) The parameter was used, but was undefined.
<frettled> I'm not eager to fight for this, since I'm not sure where
the snowball will stop rolling.
<masak> whoa, "obviously isn't the correct way"? please explain -- I
didn't know it was obvious that it isn't the correct way, let alone
that it isn't the correct way...
<masak> frettled​: for a typed parameter, the case 2) will never occur.
<frettled> masak​: ah, right, that was mentioned earlier as well.
<frettled> I used "obviously" loosely; it's not quite obvious to me
that this is the way it _ought_ to be, but it's obvious that it's the
way it _is_.
<masak> ah. :)
<masak> I think 'defined' should be used to test whether a variable,
parameter or attribute has been initialized.
<masak> I think that typing shouldn't affect this maxim.
<frettled> Yup.
<frettled> As I said before, it breaks with the principle of least
surprise, and it also breaks with the principle of not having special
exceptions for common cases.
<masak> mm.
<masak> I think I'm ready to submit a rakudobug after all.
<masak> but it'll be one helluva rakudobug... :)
* masak submits

@p6rt
Copy link
Author

p6rt commented Jul 28, 2010

@coke - Status changed from 'new' to 'open'

@p6rt
Copy link
Author

p6rt commented Aug 15, 2010

From @masak

On Tue Apr 21 06​:35​:05 2009, masak wrote​:

Apologies, the below discussion is long and involved. I've tried to
simplify and reduce as much as I can.

<masak> rakudo​: class A { multi method dispatch(@​chunks, %param?) {
say %param.perl, defined %param } }; A.new.dispatch([1,2,3]);
A.new.dispatch([1,2,3], { "OH" => "HAI" })
<p6eval> rakudo 69b318​: OUTPUT«{}1␤{"OH" => "HAI"}1␤»
<masak> I'm slightly surprised that the former is defined.
<masak> but maybe it has to be that way.
<moritz_> masak​: that's because %param is an instance
<masak> an instance?
<moritz_> objects
<jnthn> An instance of Hash
<moritz_> objects of normal classes are always defined
<masak> ok, you two don't seem to think this is an error, so I guess
it isn't :)
<frettled> masak​: I think I grokked the code in your previous example
now, and yes, think it makes sense that it works the way it did.
<masak> frettled​: with the instantiated hash?
<frettled> masak​: yep
<masak> frettled​: well, I just want a simple way to test if the
parameter was passed.
<masak> frettled​: or maybe {} is the default for Hash...
<frettled> masak​: the way I read the spec, the default values are the
ones you specify in the sub declaration.
<moritz_> frettled​: if %h were undef, you couldn't assign values to it
<masak> ack. it's more dwimmy that it's {}.
<frettled> moritz_​: hrm, that's breaking the principle of least
surprise.
<masak> but... I just want a nice way to see if the hash was passed or
not.
<moritz_> frettled​: maybe the spec needs to be updated to only refer
to scalar optional parameters
<frettled> moritz_​: or to allow assignment to undefined non-scalars ;)
<p6eval> rakudo 69b318​: OUTPUT«{"a" => 5, "b" => 2, "c" => 3}␤»
<moritz_> frettled​: that would be much weirder, overall
<frettled> moritz_​: would it?
<moritz_> frettled​: yes.
<frettled> moritz_​: I don't see how, though.
<moritz_> frettled​: if you have an undef value, it's usually not a
"normal" object, but a proto object
<moritz_> frettled​: so if a default value is such a proto object, a
%h<a> = 3 would call the postcircumfix​:<< < > >> method as a class
method
<moritz_> frettled​: which doesn't have access to attributes - so it's
going to barf
<masak> moritz_​: what does 'my %h;' do?
<moritz_> masak​: it calls Hash.new under the hood, I assume
<masak> then it can be argued that '%h?' should, too.
<frettled> moritz_​: so essentially, it's been designed into a corner
where you can't get out of without getting hacks all over your feet ;)
<moritz_> frettled​: right
<frettled> moritz_​: $expletive
<moritz_> frettled​: of course there are other options...
<frettled> moritz_​: yes, _is rw_, for instance, seems like it would
help.
* frettled is a Perl 6 spec n00b, though.
<moritz_> frettled​: like having %h? defaulting to a Hash.new(defined
=> 0) or so
<frettled> moritz_​: no, that's the wrong solution, because it still
means you can't say whether you got passed an empty hash or not.
<moritz_> frettled​: you could, because an empty hash defaults to being
defined
<frettled> ah, I misunderstood the magic
<moritz_> but there's a drawback... every modifying call to a hash
would have to set its defined flag
<frettled> mm
<frettled> I think this is a design weakness.
<moritz_> the problem is that we're doing in-band communication
<moritz_> instead of asking the hash parameter if it was defined, we
should ask the capture if that hash was given
<masak> aye.
<moritz_> especially since objects can respond with False to .defined
at will anyway
<frettled> the way you put it, it seems like there is a
straight-forward way of doing so, and that would be good.
<frettled> Conceptually, that's much cleaner than asking the variable.
<moritz_> the simplest solution that works with the current spec is to
split it into two multis
<moritz_> but that's not always convenient
<frettled> I was considering to suggest that, but I thought it would
be neater to use the optional argument as, well, optional.
<frettled> In more complex cases it would make more sense to create
multiple methods.
<frettled> (going Java)
<frettled> There doesn't appear to be a test for this in S06, though.
Unless I'm misreading the tests or missing a test, only scalars are
tested. But my test-fu is weak.
<moritz_> frettled​: it might well be missing... but let's try to get
clarification on the spec first, and then write tests :-)
<frettled> moritz_​: yep.
<frettled> moritz_​: though the spec is pretty clear about it, really,
I just had to read the right part of it.
<frettled> It's right there in the intro of S06/Parameters and
arguments/
<frettled> It speaks of "container argument" as opposed to a scalar.
<frettled> so if anyone has the test-fu to write a test for testing
whether optional hash and array parameters are 1) immutable, 2)
defined with their correct default value, and 3) handles correctly
with is rw, is copy, etc.
<frettled> ...that would be nice. Then I can leech on and see how
that's tested. :D

...meanwhile, a bit later...

<masak> rakudo​: class A { has Callable $.c; method foo() { say defined
$.c } }; A.new.foo
<p6eval> rakudo 69b318​: OUTPUT«1␤»
<masak> I must say this is highly counterintuitive.
<masak> if that one is defined, what in the world is it defined to be?
<masak> I feel both this case and the case with the default parameters
undermines thewhole concept of definedness.
<masak> to me, 'defined' should be able to show whether the variable
in question has been assigned to, initialized etc.
<masak> moreover...
<masak> rakudo​: class A { has $.c; method foo() { say defined $.c } };
A.new.foo[14​:28]
<p6eval> rakudo 69b318​: OUTPUT«0␤»
<masak> now it works as we all expect it to!
<masak> so the current behaviour is actually a kind of punishment for
adding types!
<baest> masak​: I think it's only Roles
<baest> rakudo​: class B { }; class A { has B $.c; method foo() { say
defined $.c;} }; A.new.foo;
<p6eval> rakudo 69b318​: OUTPUT«0␤»
<masak> baest​: I think you're right.
<masak> but I don't think roles should behave like this either.
<baest> indeed
<baest> would be _very_ weird
<masak> moritz_ seemed to argue earlier that the optional-params case
was correct.
<frettled> Yes, at least correct according to spec, and as far as I
could tell, it was.
<masak> right.
<masak> that's why I'm not submitting it as a rakudobug, because it's
really a spec issue.
<frettled> masak​: Yes, and I don't think I quite got an answer to how
to check whether an argument was used in any other way than doing
multiple dispatch to eliminate the alternatives.
<masak> frettled​: indeed.
<masak> this discussion is not-yet-resolved.
<frettled> Since defined() obviously isn't the correct way to do it,
an alternative method is necessary, e.g. another property for each
parameter that's specified in the sub/method definition.
<frettled> That would be quite useful, since it allows us to separate
between the following cases​:
<frettled> 1) The parameter was not used.
<frettled> 2) The parameter was used, but was undefined.
<frettled> I'm not eager to fight for this, since I'm not sure where
the snowball will stop rolling.
<masak> whoa, "obviously isn't the correct way"? please explain -- I
didn't know it was obvious that it isn't the correct way, let alone
that it isn't the correct way...
<masak> frettled​: for a typed parameter, the case 2) will never occur.
<frettled> masak​: ah, right, that was mentioned earlier as well.
<frettled> I used "obviously" loosely; it's not quite obvious to me
that this is the way it _ought_ to be, but it's obvious that it's the
way it _is_.
<masak> ah. :)
<masak> I think 'defined' should be used to test whether a variable,
parameter or attribute has been initialized.
<masak> I think that typing shouldn't affect this maxim.
<frettled> Yup.
<frettled> As I said before, it breaks with the principle of least
surprise, and it also breaks with the principle of not having special
exceptions for common cases.
<masak> mm.
<masak> I think I'm ready to submit a rakudobug after all.
<masak> but it'll be one helluva rakudobug... :)
* masak submits

<pmichaud> I've been thinking that "my %h" initializes %h to be the Hash type object
<pmichaud> (thinking recently, at least)
<pmichaud> with a WHENCE property that vivifies %h into a true Hash when it's used
<masak> ooh
<pmichaud> same for %h? as a parameter
<masak> \o/
<pmichaud> would that solve the problems identified in the ticket?
<masak> oh yes, oh yes
<pmichaud> (I'm having trouble following the issues there)
* masak dances
* masak adds this new development to ticket

@p6rt
Copy link
Author

p6rt commented Jun 20, 2013

From @masak

On Tue Apr 21 06​:35​:05 2009, masak wrote​:

<masak> but... I just want a nice way to see if the hash was passed or
not.
[...]
<moritz_> the problem is that we're doing in-band communication
<moritz_> instead of asking the hash parameter if it was defined, we
should ask the capture if that hash was given
<masak> aye.
<moritz_> especially since objects can respond with False to .defined
at will anyway
<frettled> the way you put it, it seems like there is a
straight-forward way of doing so, and that would be good.
<frettled> Conceptually, that's much cleaner than asking the variable.

<masak> lizmat​: yes, for a long time I also wanted a way to introspect
the Capture.
<masak> lizmat​: thing is, the Capture is caller-side, and when you want
to do the introspection, you're callee-side.
<jnthn> You already can keep a capture around if you want to look at it.
<jnthn> sub foo(|c, :$a, :$b) { c.exists('a') }
<masak> oh, troo.
<masak> so, there we go.
<masak> feature already in the language. :) case closed. :)
<masak> I guess we can close the RT ticket, too.

pmichaud's WHENCE solution is still not in place, but I don't see why
that should keep us from resolving the ticket.

So, resolving ticket.

@p6rt
Copy link
Author

p6rt commented Jun 20, 2013

@masak - Status changed from 'open' to 'resolved'

@p6rt p6rt closed this as completed Jun 20, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant