evalEmpire / method-signatures

Method and function declarations with signatures, no source filter
http://metacpan.org/pod/Method::Signatures
Other
33 stars 35 forks source link

modifiers for defaults (||=, //= and ''=) #45

Closed ruz closed 11 years ago

ruz commented 12 years ago

Hi,

I've implemented subj. Didn't expect it to be so easy. Only a few tests and no docs. Will accept this new feature for inclusion? Example from tests:

# Test that undef, 0 and '' override defaults
method echo($message = 'what?') { return $message }
is( Stuff->echo(),          'what?' );
is( Stuff->echo(undef),     undef   );  
is( Stuff->echo(''),        ''      );  
is( Stuff->echo(0),         0       );  
is( Stuff->echo('who?'),    'who?'  );  

# Test defaults modifiers
method echo_or ($message ||= 'what?') { return $message }
is( Stuff->echo_or(),          'what?' );
is( Stuff->echo_or(undef),     'what?' );
is( Stuff->echo_or(''),        'what?' );
is( Stuff->echo_or(0),         'what?' );
is( Stuff->echo_or('who?'),    'who?'  );  

method echo_dor($message //= 'what?') { return $message }
is( Stuff->echo_dor(),          'what?' );
is( Stuff->echo_dor(undef),     'what?' );
is( Stuff->echo_dor(''),        ''      );  
is( Stuff->echo_dor(0),         0       );  
is( Stuff->echo_dor('who?'),    'who?'  );  

method echo_eor($message ''= 'what?') { return $message }
is( Stuff->echo_eor(),          'what?' );
is( Stuff->echo_eor(undef),     'what?' );
is( Stuff->echo_eor(''),        'what?' );
is( Stuff->echo_eor(0),         0       );  
is( Stuff->echo_eor('who?'),    'who?'  );  
chloe-zen commented 12 years ago

I don't like ''= as a feature, but I like ||= and I love //=.

ruz commented 12 years ago

I find ''= very useful in web applications and databases. This modifier is my primary goal. I want \S= (treat space only string as no argument) as well, but I can live without it and use ''=. If ''= is not acceptable then I have to ask if patches that improve extendability are acceptable, so I can subclass module and implement custom modifiers without black magic?

chloe-zen commented 12 years ago

I think your real desire being to treat whitespace as missing is exactly why I thought ''= was a bad idea, besides which there is no such operator as ''=. Seems like you want type checking with coercion, which should be supportable through other means.

ruz commented 12 years ago

Hi,

My real desire is to treat empty string as undef and don't treat 0 as false value. Trimming strings from spaces is different thing.

Coercion may help, but it doesn't work at the moment:

perl -I lib/ -MMouse::Util::TypeConstraints -MMethod::Signatures -E 'coerce "Int" => from "Str" => via { /\S/? $: undef }; func foo(Maybe[Int] $a //= 10) { return $a }; eval {say foo($)} or print $@ foreach 11, "", 0, undef'

I don't think coercion should work automatically, instead it should be enabled explicitly by developer for every argument.

I'm ok with ''= excluded from the patch.

barefootcoder commented 12 years ago

I see what you're trying to do with ''=, but I'm with @chipdude : I'm a little uncomfortable with inventing new operators. I think we're going to have to throw this over to @schwern for his thoughts. My vote would be to take your ||= and //= as is, and maybe try to find a way to keep the concept of ''= and just find a better syntax for it. But schwern is the final arbiter.

schwern commented 12 years ago

I don't think coercion is appropriate for this use. While one certainly can use coercion to do it, coercion is more about "convert X format to Y format" but you retain the same basic value. You might convert the string "2012-01-21" to a DateTime object representing "2012-01-21", but you wouldn't use coercion to convert "" to a DateTime object representing "2012-01-21". Coercion is associated with a type, defaults are associated with the use. [1]

I also agree with @barefootcoder that existing assignment operators are better than making ours up. I would be more comfortable with an extended default syntax to allow people to express when to apply the default then to try and cover it with more operators. Something like...

method foo( Str $foo has default { "bar" } when { !/\S/ } ) {
    ...
}

This reflects the "where" block we'd like to incorporate from MooseX::MS... or maybe it should just be if ...but that's another show.

In short: IMO ||= and //= are cool, ''= is not, and a more generic "apply X to the argument when Y is true" syntax would be nice.

[1] I realize there's a big split between coercion being the choice of the type author or the type user, but all parties agree that how you coerce is associated with the type.

chloe-zen commented 12 years ago

schwern++ nailed it

schwern commented 12 years ago

@barefootcoder and @thoughtstream have been having a conversation about this in private email. I'm going to paste it all in here so other folks can contribute.

schwern commented 12 years ago

From: Damian Conway damian@conway.org Date: Wed, 8 Aug 2012 18:51:25 +1000 Subject: Possible patch for Method::Signatures To: barefoot@cpan.org Cc: Michael Schwern schwern@pobox.com

[Schwern CC'd, as this is a design issue]

Hi Buddy. Hi Schwern.

I adore Method::Signatures and am now using it in nearly all my own development...and in absolutely all my teaching.

However, I find myself endlessly frustrated by the constant need for:

func foo ($opt_arg?) {
    $opt_arg //= $DEFAULT;
    ...
}

In order to handle the cases like:

foo( $some_hash{'some_missing_key'} );

The problem, of course, is that undef sometimes means "use the default", but the Method::Signatures default mechanism believes that only non- existence implies defaulting.

Now, I'm the first to admit that this is completely consistent with the Perl 6 semantics, and usually makes a lot of sense. But I still grind my teeth with the frequent need to explicitly //= an optional argument within the subroutine body.

Hence the attached patch.

It adds a second kind of default to Method::Signatures, so that instead of:

func foo ($opt_arg?) {
    $opt_arg //= $DEFAULT;
    ...
}

you can just write:

func foo ($opt_arg //= $DEFAULT) {
    ...
}

I submit it for your consideration, with the full disclosure that we have not approved such a mechanism for Perl 6. And that Larry may well never allow it. Personally, I find it natural and obvious, but I leave the decision entirely up to you both. >;-)

All the best,

Damian

schwern commented 12 years ago

From: Buddy Burden barefootcoder@gmail.com Date: Wed, 8 Aug 2012 09:47:48 -0700 Subject: Re: Possible patch for Method::Signatures To: Damian Conway damian@conway.org Cc: barefoot@cpan.org, Michael Schwern schwern@pobox.com

Damian,

It adds a second kind of default to Method::Signatures, so that instead of:

func foo ($opt_arg?) {
    $opt_arg //= $DEFAULT;
    ...
}

you can just write:

func foo ($opt_arg //= $DEFAULT) {
    ...
}

Actually, you're the second person to make this suggestion, and, reviewing the discussion[1], it appears that we tentatively decided to go ahead with it, and then just dropped the ball. If you have the time, I'd love to hear your thoughts on the discussion and also the patch[2] that Ruz submitted.

I should be able to get one of the two patches (or some combination thereof) applied and pushed out moderately quickly once we come to concensus.

        -- Buddy

[1] https://github.com/schwern/method-signatures/issues/45 [2] https://github.com/schwern/method-signatures/pull/46

schwern commented 12 years ago

From: Damian Conway damian@conway.org Date: Thu, 9 Aug 2012 08:38:19 +1000 Subject: Re: Possible patch for Method::Signatures To: Buddy Burden barefootcoder@gmail.com Cc: Michael Schwern schwern@pobox.com

Thanks, Buddy.

I agree with you and Schwern and chipdude:

//=   - yes
||=   - maybe
''=   - a step too far (no new operators!)

Actually, I worry that even ||= is a step too far: as it's more familiar to most people and as it has previously been (mis)used for defaulting, I think it's fraught with some peril. Perl has traditionally treated non-existence or undefinedness as its "default me now, baby" signals, for good reasons. Defaulting on defined (i.e. in-band) values is a risky proposition, I think. That's why I only suggested //= in my own patch.

I also agree with Schwern that the prospect of four separate defaulting syntaxes means that we really need a smart-matched metasyntax.

As for the patches themselves, I think my own is slightly cleaner, more appropriately modest in its ambitions, and has a better test suite. But then, I would think that, wouldn't I? ;-)

I'd be completely fine with using Ruz's instead, but only if the ||= and ''= were removed, and you used my test suite.

Damian .

schwern commented 12 years ago

From: Buddy Burden barefootcoder@gmail.com Date: Wed, 8 Aug 2012 22:48:36 -0700 Subject: Re: Possible patch for Method::Signatures To: Damian Conway damian@conway.org Cc: Michael Schwern schwern@pobox.com

Damian,

Thanks for the cogent analysis!

Actually, I worry that even ||= is a step too far: as it's more familiar to most people and as it has previously been (mis)used for defaulting, I think it's fraught with some peril. Perl has traditionally treated non-existence or undefinedness as its "default me now, baby" signals, for good reasons. Defaulting on defined (i.e. in-band) values is a risky proposition, I think.

Well, my only counter to that is that ||= already has a pretty well-defined meaning, so people who use it really oughtn't be surprised by its action, especially if the only reason they even know it exists is because they read it in the POD right underneath where we explain what //= means. ;-> But your point is well-reasoned, and I don't really have a horse in this race--so far I've needed neither.

As for the patches themselves, I think my own is slightly cleaner, more appropriately modest in its ambitions, and has a better test suite. But then, I would think that, wouldn't I? ;-)

So my proposal would be to take your patch, apply it, and let's leave it there and see if Ruz can live with that.

func foo ($fred = 42) { say $fred }
func bar ($fred //= 42 { say $fred }

foo();         # 42
bar();         # 42

my $x;
foo($x);              # uninitialized warning
bar($x);              # 42
bar($x || undef);  # just like ||= !

I'll see if schwern wants to chime in to agree or disagree.

Say, while I have your attention, mind if I ask your opinion on another MS matter, since you seem to be a heavy MS user? What do you think this should mean?

method foo {}

'Cause I've noticed that this is another discrepancy between MS/MSM and MXMS. That is, we think it should mean:

method foo () {}

whereas mst and Florian and the rest of the Moosites seem to think it should mean:

method foo (@_) {}  # not that MXMS actually supports this syntax,

but you know what I mean

And, after using MS (and especially MSM) in some real code for a while now, I'm starting to lean towards their way of thinking. Especially when I'm wrapping internal methods (e.g. BUILDARGS in Moose, or usage_error in MooseX::App::Cmd), sometimes I don't have a very clear idea just what the heck they're passing in, and I always end up blowing up with the "too many args" error.

What's your opinion on this?

        -- Buddy

.

schwern commented 12 years ago

From: Damian Conway damian@conway.org Date: Thu, 9 Aug 2012 19:01:27 +1000 Subject: Re: Possible patch for Method::Signatures To: Buddy Burden barefootcoder@gmail.com Cc: Michael Schwern schwern@pobox.com

Hi Buddy,

Well, my only counter to that is that ||= already has a pretty well-defined meaning, so people who use it really oughtn't be surprised by its action, especially if the only reason they even know it exists is because they read it in the POD right underneath where we explain what //= means. ;->

Fair point. Except that hasn't stopped generations of Perl programmers misusing it to date (which is why we invented //= in the first place ;-)

So my proposal would be to take your patch, apply it, and let's leave it there and see if Ruz can live with that.

Well, I'm certainly not going to disagree with that course of action! :-) I suspect Ruz will not be content.

So...that got me thinking (and hence coding)...

Why should my construct be accepted, but not Ruz's. Maybe my special case isn't special enough either. Or maybe it's too special. Maybe we don't need //= either.

Because maybe we can just have:

sub foo ($arg = 'default')               # existence test
sub foo ($arg = 'default' when undef)    # default if $arg ~~ undef
sub foo ($arg = 'default' when '')       # default if $arg ~~ ''
sub foo ($arg = 'default' when 0)        # default if $arg ~~ 0
sub foo ($arg = 'default' when * < 0 )   # default if $arg < 0

That's not actually quite as convenient for my own needs, but I'd be most happy to live with it, given that it's so much more generalizable.

Sample patch attached. (I know, I know, now I've merely made the original decision harder ;-)

I'll see if schwern wants to chime in to agree or disagree.

I'd certainly like to hear his views as well.

Especially about my latest "use-=-only-but-add-smartmatching" alternative.

Say, while I have your attention, mind if I ask your opinion on another MS matter, since you seem to be a heavy MS user? What do you think this should mean?

method foo {}

Well, I would start from the philosophical position that MS should mirror Perl 6 as far as is sensible in Perl 5. Unfortunately, in Perl 6, no argument list means either:

method foo () {...}

or:

method foo (@_) {...}

depending on whether @_ is referred to within the subroutine body.

So that's no help. %-(

Personally, if it can't mirror Perl 6 ehaviour, then I think it should mirror Perl 5 semantics, in which case:

method foo {...}
  func bar {...}

should mean:

method foo ($self, @_) {...}
  func bar (@_) {...}

and you'd have to say:

method foo () {...}
  func bar () {...}

to get the zero-argument forms.

So I guess I'm siding with mst and Florian and the other cervinophiles.

HTH,

Damian

schwern commented 12 years ago

From: Buddy Burden barefootcoder@gmail.com Date: Thu, 9 Aug 2012 11:53:34 -0700 Subject: Re: Possible patch for Method::Signatures To: Damian Conway damian@conway.org Cc: Michael Schwern schwern@pobox.com

Damian,

Because maybe we can just have:

sub foo ($arg = 'default')               # existence test
sub foo ($arg = 'default' when undef)    # default if $arg ~~ undef
sub foo ($arg = 'default' when '')       # default if $arg ~~ ''
sub foo ($arg = 'default' when 0)        # default if $arg ~~ 0
sub foo ($arg = 'default' when * < 0 )   # default if $arg < 0

That's not actually quite as convenient for my own needs, but I'd be most happy to live with it, given that it's so much more generalizable.

Well, that's very close to what schwern was thinking-out-loud in the original discussion with Ruz, so, if you've made that work,() I can't see anything to object to there. Well, except for maybe the "when \ < 0" ... that one looks a little funky to me. And I suppose I would wonder what would happen if we tried to run this on Perl prior to ~~ (pre-5.10, right?). Just wouldn't compile?

Personally, I've given up trying to run MS on pre-5.10 (or pre-5.12, even). The whole Devel::Declare thing just gets wonky on those older Perl's. It's the main reason I've been able to push my company into getting off of 5.8.9 after lo these many years. (Although admittedly that's still a work in progress.) But my point being, I personally wouldn't have a problem having a newer version of Perl be a requirement for MS altogether. But I think schwern doesn't want to do that.

I'll see if schwern wants to chime in to agree or disagree.

I'd certainly like to hear his views as well.

Me too, but I think he's buried in Test::Builder stuff. At least, that appears to be the only thing I see him raise his head for these days. But, who knows? Maybe we'll entice him out yet. ;->

: : and you'd have to say:

method foo () {...}
  func bar () {...}

to get the zero-argument forms.

So I guess I'm siding with mst and Florian and the other cervinophiles.

(**) Excellent! I think I'll code up this change in a branch and see if I can get schwern to agree to it. Maybe I'll also do a blog post on it to see if any other MS users want to chime in.

        -- Buddy

(*) I fully admit that I haven't actually had a chance to look at your code yet. But obviously I trust that it works.

(**) Heheh. "Cervinophiles" ... nice one. Although, in order to distinguish among lovers of Bambi and fans of Santa's propulsion system and whatnot, perhaps something more specific? Alcinophile, perhaps? No, that would be proponents of puffins and auks ... (***)

(***) Yes, I had to look those last two up. But Cervidae I knew ... honest! :-D

schwern commented 12 years ago

From: Damian Conway damian@conway.org Date: Fri, 10 Aug 2012 11:46:18 +1000 Subject: Re: Possible patch for Method::Signatures To: Buddy Burden barefootcoder@gmail.com Cc: Michael Schwern schwern@pobox.com

Buddy,

Well, that's very close to what schwern was thinking-out-loud in the original discussion with Ruz, so, if you've made that work,() I can't see anything to object to there. Well, except for maybe the "when \ < 0" ... that one looks a little funky to me. And I suppose I would wonder what would happen if we tried to run this on Perl prior to ~~ (pre-5.10, right?). Just wouldn't compile?

The "when * < 0" syntax is borrowed from Perl 6, where it's a standard and widely used feature. But I agree that it might be a little too funky for MS. See the attached next-generation patch, which uses a Perl 5 compatible block-and-$_ syntax instead. Much more appropriate, I think.

Note that, compared to the previous submission, the attached patch also has more robust parsing of 'when' modifiers.

Personally, I've given up trying to run MS on pre-5.10 (or pre-5.12, even). The whole Devel::Declare thing just gets wonky on those older Perl's. It's the main reason I've been able to push my company into getting off of 5.8.9 after lo these many years. (Although admittedly that's still a work in progress.) But my point being, I personally wouldn't have a problem having a newer version of Perl be a requirement for MS altogether. But I think schwern doesn't want to do that.

I admire the dedication to supporting ancient Perl versions, though I refuse to use or support them myself now. I'm confident the attached patch isn't a problem under 5.8. The attached version now detects the Perl you're running and reports an error if the "when..." construct is used under 5.8 or less. It should have no ill effects so long as 5.8-users don't use the "when" feature.

I'd certainly like to hear his views as well.

Me too, but I think he's buried in Test::Builder stuff. At least, that appears to be the only thing I see him raise his head for these days. But, who knows? Maybe we'll entice him out yet. ;->

Fair enough. I'll keep submitting more and more outrageous patches and you keep threatening to apply them without proper scrutiny, and I'm sure he'll soon be forced to step in. >;-)

So I guess I'm siding with mst and Florian and the other cervinophiles.

(**) Excellent! I think I'll code up this change in a branch and see if I can get schwern to agree to it.

I'd be all in favour of that.

(*) I fully admit that I haven't actually had a chance to look at your code yet. But obviously I trust that it works.

Hmmmmm. Long and bitter experience indicates I'm every bit as fallible as a real human. Better check that code. ;-)

(**) Heheh. "Cervinophiles" ... nice one. Although, in order to distinguish among lovers of Bambi and fans of Santa's propulsion system and whatnot, perhaps something more specific? Alcinophile, perhaps? No, that would be proponents of puffins and auks ... (***)

(***) Yes, I had to look those last two up. But Cervidae I knew ... honest! :-D

"Alces" (in classical pronunciation, at least) is very close to "Alkies", which is our Aussie slang for "Alcoholics". So maybe the Moose-lovers are "Elkoholics"? Though, come to think about it, "Moose-lover" is a pretty good epithet itself. ;-) Damian
schwern commented 12 years ago

From: Buddy Burden barefootcoder@gmail.com Date: Fri, 10 Aug 2012 13:13:19 -0700 Subject: Re: Possible patch for Method::Signatures To: Damian Conway damian@conway.org Cc: Michael Schwern schwern@pobox.com

Damian,

The "when * < 0" syntax is borrowed from Perl 6, where it's a standard and widely used feature.

Oh. Well, that's ... nice. I'm sure that, you know, in the proper context ....

But I agree that it might be a little too funky for MS.

Whew! :-D

Yeah, while generally I love doing little things in my code that prepare me for a theoretical transition to Perl 6 one day (I'm actually very fond of some of your own modules for that, such as Perl6::Slurp and Perl6::Form), I do think that, in the end, it's a different language, and I'm not sure that there are some things which are going to work well in a Perl 5 context. I've been very pleased to see some things backported (e.g. ~~, given/when, etc), and I actually hope to see a few more (==> and <==, especially, if those are still the appropriate operators in the P6 spec), but then there are some things which I'm a little more leery of. This one I hadn't actually seen before (or else I have and I just blocked it out of my mind :D ), but it struck me as odd.(*)

See the attached next-generation patch, which uses a Perl 5 compatible block-and-$_ syntax instead. Much more appropriate, I think.

Rock on.

I'm confident the attached patch isn't a problem under 5.8. The attached version now detects the Perl you're running and reports an error if the "when..." construct is used under 5.8 or less. It should have no ill effects so long as 5.8-users don't use the "when" feature.

Excellent!

I'd certainly like to hear his views as well.

Me too, but I think he's buried in Test::Builder stuff. At least, that appears to be the only thing I see him raise his head for these days. But, who knows? Maybe we'll entice him out yet. ;->

Fair enough. I'll keep submitting more and more outrageous patches and you keep threatening to apply them without proper scrutiny, and I'm sure he'll soon be forced to step in. >;-)

There ya go. ;->

(*) I fully admit that I haven't actually had a chance to look at your code yet. But obviously I trust that it works.

Hmmmmm. Long and bitter experience indicates I'm every bit as fallible as a real human. Better check that code. ;-)

Oh, yes, definitely. I just meant that, given my experience with your work, I didn't feel the need to scrutinize the code before discussing it. Reputation has to be good for something, no? :-)

But, yeah, before I go willy-nilly applying it, I'll dig in and give it a critical eye. Oh, no ... wait ... I mean, no, I'm just going to slap it in there without even looking! (Think that worked? ;-> )

"Alces" (in classical pronunciation, at least) is very close to "Alkies", which is our Aussie slang for "Alcoholics". So maybe the Moose-lovers are "Elkoholics"?

Oooh, that's a nice one as well. Especially since my understanding is that what we Americans call a moose is referred to as an elk in Europe. I'll have to steal that one. ;->

        -- Buddy

() So I guess this is an application of * as the "whatever" operator? So \ in P6 is multiplication, flattening, regex quantifier, "whatever," and isn't it also involved in the hyper operations somehow? A lot of semantic overloading on the poor asterisk ...

schwern commented 12 years ago

From: Damian Conway damian@conway.org Date: Sat, 11 Aug 2012 12:29:08 +1000 Subject: Re: Possible patch for Method::Signatures To: Buddy Burden barefootcoder@gmail.com Cc: Michael Schwern schwern@pobox.com

() So I guess this is an application of \ as the "whatever" operator?

Yes.

So * in P6 is multiplication, flattening, regex quantifier, "whatever," and isn't it also involved in the hyper operations somehow?

Yes to all, except the hyperoperations. Also, it's the "globally scoped" twigil (e.g. $_ARGS, %_ENV, etc.)

A lot of semantic overloading on the poor asterisk ...

Hey, at least it no longer has to be the typeglob sigil! ;-)

One of our main problems in designing Perl 6 was the paucity of ASCII symbolic characters with which to specify the dozens of new features we wanted to add. The result is that most of them end up doing double-, triple-, or occasionally even more duties. Even if we discount regex meanings and twigils, all of the following still have multiple meanings in Perl 6: ~ + % & * = [] {} ; : <> /

On the other hand, most of those have multiple meanings in Perl 5 too. So maybe it's a case of the pot calling the kettle "confusing". ;-)

Moreover, in designing Perl 6, we tried very hard to make the multiple meanings either consistent (like ~ being binary concatenate-strings and unary coerce-to-string and + being binary add-numbers and unary coerce-to-number), or else consistent with existing Perl 5 (ab)usage.

Sure your can find bad edge-cases:

my $multiplier = * * *;

but you can do worse in Perl 5:

my $zero_value = <=> <=> <=>;

;-)

Meanwhile, on an unrelated topic: the one other thing that really bugs me about the current interface of MS compared to that of Perl 6 or MXMS is the inability to specify of type constraints via 'where' modifiers within the signature.

The introduction of 'when' modifiers would make this a little more tricky to implement robustly, but if (in a week or two) I sent you a patch for 'where' constraints, would you consider it?

Damian

schwern commented 12 years ago

Sorry to @barefootcoder and @thoughtstream (who is Damian if folks didn't know) for having that all repeated in their inbox. Now everyone is up to speed and can participate. @thoughtstream, if you're not comfortable with submitting patches as pull requests, you can "attach" them by pasting them into http://gist.github.com and putting the link in here (unless the Github Issues email gateway does something REALLY clever with email attachments).

Several issues have gotten mixed up in here, so I want to tease them apart. This issue is already complicated and I want to shunt these off to their own issues.

First, what should a method with no signature do? We discussed this before, so might as well reopen that one. https://github.com/schwern/Method-Signatures/issues/26

The when syntax IMO needs more discussion. Also it should be considered separately from //=. //= is a clear win and slides easily into the existing default syntax. when has parsing edge cases, a default is any valid expression. I've cracked open a new issue for that, please discuss it there. https://github.com/schwern/method-signatures/issues/48

Ok, so... yes to ||= and //=. I hear Damian's argument about ||= being abused a lot. I don't really know if I agree with it, but we don't have the data. What I do agree with is that //= is the far more useful operator. That's the one I care about seeing. If ||= is implemented as well, I don't see any real harm. If it's not, it can be added later. So I say leave that up to the implementer.

As to whose implementation should win, now @ruz and @thoughtstream can collaborate if they want to. Otherwise, take the first complete patch and fix it up if necessary.

thoughtstream commented 12 years ago

I'm eager to discuss the semantics of missing signatures but https://github.com/schwern/Method-Signatures/issues/26 has not been reopened yet.

thoughtstream commented 12 years ago

I note the new thread for discussing when, but I think some of that discussion belongs here as well.

The reason I suggested when modifiers instead of //= and ||= is that I belive it's a much more momentous decision to move from a single default indicator to two (or three). Especially when that move encourages people to suggest ''= as well!

The when proposal covers all those cases (and infinitely more cases besides) without multiplying entities unnecessarily. Yes, it has parsing issues, but I do not think they are insurmountable. And, given that Method::Signatures already uses PPI, we might well deploy that module to handle the hard cases here as well.

In Perl 6, we (have so far) resisted //= as an argument default marker, and I wonder whether, in Perl 5, we shouldn't perhaps resist it too?

schwern commented 12 years ago

The parsing issues can be overcome, I'm sure. ||= and //= are syntax conveniences for the two most common defaults. They're simpler and shorter to express, but more importantly, they're existing Perl 5 assignment operators. The answer to, "how do I write the equivalent of $arg //= 42" is "$arg //= 42" not "$arg = 42 when undef" which is a new kind of statement modifier we just made up.

Other than the // and || cases, I don't see a general purpose default condition being used very often. Make generic conditions possible, but make the most common version as convenient and obvious as possible.

I don't see how having only one default assignment operator is worth making the user use a statement modifier we just made up. I also don't see why having only one default assignment operator is valuable, aside from some vague language principles.

thoughtstream commented 12 years ago

Err...you keep saying that when COND is something "we just made up", but it isn't. Postfix when is a standard feature of Perl (from Perl 5.14). As described in http://perldoc.perl.org/perlsyn.html#Switch-Statements.

As for the general purpose default condition not being used very often, Ruz already gave an excellent use-case for when ''. And I think I could make a pretty solid argument for = 1 when { $_ <= 0 }, which I seem to need all the time.

Finally, the language principles here are not "vague"; they are very clear: Having one way to do something (i.e. specify a default with =) is easy to teach; Having three ways (= vs //= vs ||=) is considerably harder to teach. Especially when one of those three ways is usually a mistake, and the other two are diametrically opposite in effect, depending on what undef means to you.

Moreover, having three defaulting indicators introduces the requirement to make a choice every time, which is never a good thing.

Moreoverer, I worry that those three are the thin edge of the stiletto: if we have three defaulting operators, why not four, or five, or twenty?

Personally, I think one default marker (=) and one default modifier (when COND) is the right balance. I'd be okay with = plus //= plus when COND, and would grit my teeth manfully and swallow = plus //= plus ||= plus when COND, if I must.

And I guess that would, at least, give me something new to proscribe in PBPv2. ;-)

barefootcoder commented 12 years ago

While I agree with @thoughtstream on most of his points, I'm not sure I agree that it would be harder to teach. If I were teaching it, I would teach the when first, and then just explain that //= was a convenient shortcut for when undef. Trez TMTOWTDI. If when undef is the most common case, why not have a shortcut for it? Particularly one which seems blatantly obvious on the face of it what it does.

I do agree that ||= might be a bridge too far though. One shortcut is convenient. Two, three, or a dozen can get confusing. So my vote is: keep the when, keep the //=, lose the rest.

But I'm willing to be convinced otherwise. :-)

thoughtstream commented 12 years ago

So my vote is: keep the when, keep the //=, lose the rest.

Seconded.

I'll implement the next version of the patch along those lines, and we can wait to see what consensus emerges and then prune or extend it as necessary.

ruz commented 12 years ago

On Sunday, August 12, 2012, thoughtstream wrote:

So my vote is: keep the when, keep the //=, lose the rest.

Seconded.

I'll implement the next version of the patch along those lines, and we can wait to see what consensus emerges and then prune or extend it as necessary.

'when COND' is good. It was just too big yak to shave for me, so I ended with this patch. If 'when' is implemented then I don't care about ||=, //= or other variations around =.

I can not think of a good example, but believe that it may be useful to know inside CODE whether argument is provided or not. I suspect that $_ would be set to undef when argument is undef or is not provided. 'provided' function or something like that can be helpful.

— Reply to this email directly or view it on GitHubhttps://github.com/schwern/method-signatures/issues/45#issuecomment-7674386.

Best regards, Ruslan.

thoughtstream commented 12 years ago

[Ignore the following. It doesn't make sense. See the next two comments for details.] [And remember, boys and girls, don't post comments more than three hours after your normal bedtime!]

I can not think of a good example, but believe that it may be useful to know inside CODE whether argument is provided or not.

A very good point.

Currently the plan is that, within a when test, the argument that was passed to the parameter will be both aliased to $_ and also passed directly to the when block/sub. So, an absent arg would set $_ to undef but pass no arg to the when sub. Which means you could test whether the argument was passed or not, by testing !@_ within the when block.

For example:

~~`# undef is a valid setting for the error handler,

but a missing argument is not (this is tested by: !@_)

# and a regex is not either (this is tested by: ref eq 'Regexp')...`~~

~~func set_error_handler ( $handler = $DEF_HANDLER when { !@_ || ref eq 'Regexp' } ) {...}~~

Nope. The use of a nested @_ is just too fugly and confusing (and it just feels wrong too).

So maybe a missing argument is indicated by aliasing (and passing) a special object that is blessed into a hightly distinctive and descriptive class. Like so:

~~`# undef is a valid setting for the error handler,

but a missing argument is not (this is tested by: ref eq 'NO ARG')

# and a regex is not either (this is tested by: ref eq 'Regexp')...`~~

~~func set_error_handler ( $handler = $DEF_HANDLER when { ref ~~ ['NO ARG', 'Regexp' ] } ) {...}~~

Hmmmm. I'll code that into the patch and see how it feels. 'NO ARG' seems like a pretty safe class name to use; it's very unlikely anyone is going to be using that as a real class name in their code.

barefootcoder commented 12 years ago

Wait, a default value which isn't supposed to apply when the argument isn't passed? You guys are going to have to come up with a reasonable use case before I agree that that's a good idea ...

Not to be harsh or anything; I'd just like to know that this is not a solution in search of a problem before anyone spends any time on it.

thoughtstream commented 12 years ago

Fear not, @barefootcoder, no-one is suggesting that defaults could ever be skipped if the arg isn't passed.

I was just not thinking straight (because I was thinking late) and didn't realize that @ruz's edge-case can't exist.

The proposal is still that a when modifier is only checked if the argument was actually passed. If no argument was passed, the default is always applied, without bothering to test the when criterion.

That means that, if you end up executing the block of a when CODE test and $_ is undefined, an actual undef must have been passed. If no argument had been passed, the default would have been applied automatically and unconditionally, so the when's block wouldn't have executed at all.

I've now struck out the previous comment to emphasize that. Sorry for the confusion.

barefootcoder commented 12 years ago

The proposal is still that a when modifier is only checked if the argument was actually passed. If no argument was passed, the default is always applied, without bothering to test the when criterion.

Okay, that makes a lot more sense. :-) I'm glad that's the case, because I really wasn't seeing how it made sense the other way. ;->

schwern commented 12 years ago

@thoughtstream Sorry, I didn't know that postfix when is a Perl 5 feature! That makes me happier.

I don't think the teaching issue is a simple number of assignment operators so long as we're talking about existing and commonly used assignment operators. But as I've said before, when and blah= are separate issues in my mind.

The behavior of when if nothing is passed in (ie. it acts like a normal default) seems sensible.

barefootcoder commented 11 years ago

Fixed in version 20121201.