develooper Front page | perl.perl5.porters | Postings from September 2012

Re: fixing smartmatch just hard enough (and when, too)

Thread Previous | Thread Next
From:
Father Chrysostomos
Date:
September 2, 2012 18:03
Subject:
Re: fixing smartmatch just hard enough (and when, too)
Message ID:
7BDD9006-B78B-4164-A1BE-21D2AA42D8D1@cpan.org

On Sep 2, 2012, at 4:28 PM, Ricardo Signes wrote:

> * Father Chrysostomos <sprout@cpan.org> [2012-09-02T19:06:43]
>>> It seems likely.  After all, when already only *usually* means smartmatch.
>>> For example, when($x ~~ $y) is boolean, not "$_ ~~ ($x ~~ y)".  when($x &&
>>> $y) means "$_~~$x && $_~~$y"
>> 
>> No, actually it means $_ ~~ ($x && $y), making it more useless than ever.
>> 
>> It means, Match against $x if it is true; match against $y otherwise.
> 
> A reading from perlsyn:
>> Furthermore, Perl inspects the operands of logical operators to decide
>> whether to use smartmatching for each one by applying the above test to
>> the operands:
>> 
>> 9.  If EXPR is "EXPR1 && EXPR2" or "EXPR1 and EXPR2", the test is
>>     applied recursively to both EXPR1 and EXPR2.  Only if both operands
>>     also pass the test, recursively, will the expression be treated as
>>     boolean.  Otherwise, smartmatching is used.

‘Is used’ doesn’t say to which expression or expressions smartmatch is applied.  Yes, I originally thought when($a && $b) meant when ($_ ~~ $a && $_ ~~ $b), but when looking into making it deparse properly I found that not to be the case.
> 
> And a program to demonstrate it:
> 
>    use 5.16.1;
> 
>    package SM {
>      use overload 'bool' => sub { return },
>                   '~~'   => sub { 1 };
>    }
> 
>    my $sm = bless {}, 'SM';
> 
>    say($sm ? 'sm is true' : 'sm is false');
> 
>    given ($sm) {
>      when ($sm && $sm) { say 'when was entered' }
>    }
> 
> Am I missing something, or are you?

In that example, we effectively have:

    $sm ~~ ($sm && $sm)

The boolean falsity causes the $sm on the rhs of && to be used, which returns true for ~~.  It makes no difference if you use the same thing on both sides of &&.

Look at this one-liner:
$ perl -lE '$o=1; $z=0; given(0) { when($o && $z) { print "matched" } };'
matched

Surely 0~~1 and 0~~0 can’t both be true at the same time.

> Back to Sprout:
>> [ ... ]
>> What about objects that are not overloaded?  What about qr//, which returns
>> an object?
> 
> qr// objects are made ot behave like =~ qr//.
> 
> Objects that are not overloaded remain fatal.
> 
>> If we respect qr and &{} overloading, or if non-overloaded objects just
>> croak, I still maintain that this is going to cause problems for module
>> authors that want to add extra functionality to existing objects without
>> breaking code that uses the modules.
> 
> Way back when, around 5.10.x, I argued that we should require ~~ overloading
> for ~~, not fall back to anything else.  I think I was right then and merely
> forgot about it until recently.
> 
>> Maybe the only reasonable chart would be this:
>> 
>> 1. !defined($rhs)
>> 2. overload::Method($rhs, "~~");
>> 3. reftype($rhs) eq 'CODE'
>> 4. reftype($rhs) eq 'REGEXP'
>> 5. ?
> 
> Right.  It avoids (a) ambiguity (b) breaking code when you /had/ &{} but /add/
> qr and /not/ ~~.  This was the concern then, too, but with regard to %{} and
> @{} and object guts.

And allows for blessed CODE thingies that are blessed solely to provide extra methods.  And allows reblessed Regexps to continue working.

> 
>> Should 5 be a deprecation warning?  Or just a croak?
> 
> I'd just make it fail.  It's like saying "push $hashref" ;)

(You haven’t forgotten, have you, that I’d like to made that equivalent to push %$hashref?)

>>> Under FC's proposal, which would eliminate ~~ entirely:
>>> 
>>>  given ($input) {
>>>    when (1)   { ... } # if $input == 1
>>>    when ('x') { ... } # if $input eq 'x'
>>>    when ($x)  { ... } # if $x
>>>    when (undef)  { ... } # if undef
>> 
>> Of course, one would write when (!defined).
> 
> Of course!  Think-o. :)
> 
>>>    when ($x + 2) { ... } # if ($x+2)
>>>    when ($_ > 1) { ... } # if ($_ > 1)
>>>  }
>> 
>> We already have many operators that imply $_.  Maybe what we really ought to
>> be doing is extending that.  If we can come up with new ways to imply $_ in
>> arbitrary expressions, then if() would benefit, too.
> 
> Any suggestions?

No, not yet. :-)

I thought by mentioning that I could get people to help brainstorm.

For qr// and undef we already have /$qr/ and !defined.

For sub calls? ...

> 
>> And, since you have not answered it yet, should given respond to next?
> 
> Almost certainly.  Was there ever any given (ha ha) reason not to?  Is it fear
> of changing the behavior of existing givens inside loops?

I think Zefram raised exactly that objection a year ago.

>> And here is another aspect of when/default that makes the way it breaks
>> special:
>> 
>> for (3) {
>>  for my $x (4) {
>>    when (3) {}
>>  }
>>  warn "exited inner";
>> }
>> warn "exited outer";
>> 
>> That prints "exited outer at - line 7".  So when doesn’t just do ‘next’
>> inside for.
>> 
>> I think that should change.
> 
> Well, the issue is that when interacts with a topicalizer.

Except for when{}{}. :-)  But I hope we don’t make when{}{} and when(){} break differently.

> It is going to do
> its work based on the topicalization that has occurred, and so having its
> implicit break behave like a 'next' on the relevant topicalizer is not
> obviously nuts.

A sub called from within for(...){...} could do local $_, while(<>) { .... }.  Currently that will exit the caller’s loop.


Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About