# New Ticket Created by "Carl Mäsak" # Please include the string: [perl #64928] # in the subject line of all future correspondence about this issue. # <URL: http://rt.perl.org/rt3/Ticket/Display.html?id=64928 >
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