Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?
 
PerlMonks  

converting from switch to given-when

by jmlynesjr (Deacon)
on Sep 25, 2012 at 01:29 UTC ( [id://995476]=perlquestion: print w/replies, xml ) Need Help??

jmlynesjr has asked for the wisdom of the Perl Monks concerning the following question:

Monks:

In cleaning up my More wxPerl Examples code in preparation to moving it to github, I came across several examples that use the switch construct. I want to update these to use the given-when construct. As seen below, I got a version to work, but there has to be a simpler/cleaner syntax. It seems to be something to do with (de)referencing the constant.

Thanks in advance, James

wxID_YES, wxID_NO, and wxID_CANCEL are wxPerl constants from use Wx qw(:everything).

Original working construct

use switch; switch ($selection) { case wxID_YES {$self->Wx::LogStatus ("You pressed: \ +"Yes\" ")} case wxID_NO {$self->Wx::LogStatus ("You pressed: \" +No\" ")} case wxID_CANCEL {$self->Wx::LogStatus ("You pressed: \" +Cancel\" ")} }

Replaced switch construct with given-when construct - fails, always takes Yes path

given ($selection) { when (wxID_YES) {$self->Wx::LogStatus ("You pressed: + \"Yes\" ")} when (wxID_NO) {$self->Wx::LogStatus ("You pressed: +\"No\" ")} when (wxID_CANCEL) {$self->Wx::LogStatus ("You pressed: +\"Cancel\" ")} }

Working given-when construct

given ($selection) { when ($_ == wxID_YES) {$self->Wx::LogStatus ("You pre +ssed: \"Yes\" ")} when ($_ == wxID_NO) {$self->Wx::LogStatus ("You pres +sed: \"No\" ")} when ($_ == wxID_CANCEL) {$self->Wx::LogStatus ("You pres +sed: \"Cancel\" ")} }

It also works if the constant is assigned to a scaler and then the scaler is used in the when clause.

my $yes = wxID_YES; when ($yes) {....}
Update1:

Thanks to Athanasius for the great explanation and test code. I had seen the constant as subroutine construct, but didn't know how it worked in this case(hidden smart match). Thanks also to tobyink. The when ([wxID_YES]) {....} also works (why?) and seems cleaner than what I had come up with. I will go with the bracket construct. Thanks also to Anonymous Monk for the reference to the bug report.

Update2:

Thanks again to Athanasius and tobyink. The "why?" is now answered very clearly. I had tried when ((wxID_YES)) and when ({wxID_YES}). I guess one more try and I wouldn't have asked the question and I still wouldn't have known why it worked! There are a lot of good teachers out there willing to share their time and knowledge. Thanks!

Replies are listed 'Best First'.
Re: converting from switch to given-when
by Athanasius (Archbishop) on Sep 25, 2012 at 03:03 UTC
    It seems to be something to do with (de)referencing the constant.

    Actually, running with perl -MO=Deparse shows that wxID_Yes, wxID_No, and wxID_CANCEL are subroutines:

    given ($selection) { when (wxID_YES()) { print qq[You pressed: "Yes"\n]; } when (wxID_NO()) { print qq[You pressed: "No"\n]; } when (wxID_CANCEL()) { print qq[You pressed: "Cancel"\n]; } }

    And the documentation says that “A user-defined subroutine call or a method invocation” is treated as a boolean, meaning “true” if it returns any non-zero value! (You can easily confirm this by making your own subroutines that return 0 and 1, say, and you will see that the first when clause with the non-zero-returning sub is always successful.)

    Update: Here’s the code I used for experimenting:

    So, it looks as though this (strange) behaviour is exactly as expected (until changed in a later version of Perl?) In the meantime, either of the workarounds you suggest will get the job done. I haven’t found a “simpler/cleaner syntax.” :-(

    Hope that helps,

    Athanasius <°(((><contra mundum

      Indeed. If they were proper constants (e.g. defined by constant) then they'd work with given/when. But they are not. :-(

      perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'
Re: converting from switch to given-when
by tobyink (Canon) on Sep 25, 2012 at 06:07 UTC

    Try this:

    given ($selection) { when ([wxID_YES]) { $self->Wx::LogStatus('You pressed: "Yes"' ) + } when ([wxID_NO]) { $self->Wx::LogStatus('You pressed: "No"' ) + } when ([wxID_CANCEL]) { $self->Wx::LogStatus('You pressed: "Cancel"') + } }
    perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'

      PS: if there's any possibility that $selection could potentially contain an arrayref, then my solution might behave a little oddly; you could explicitly protect against arrayrefs:

      sub is_arrayref (_) { ref(shift) eq 'ARRAY' } given ($selection) { when (is_arrayref) { die "unexpected!" } when ([wxID_YES]) { $self->Wx::LogStatus('You pressed: "Yes"' ) + } when ([wxID_NO]) { $self->Wx::LogStatus('You pressed: "No"' ) + } when ([wxID_CANCEL]) { $self->Wx::LogStatus('You pressed: "Cancel"') + } }

      Of course, probably the code that generates $selection can never generate an arrayref, so you don't need to protect against that possibility.

      perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'g
Re: converting from switch to given-when
by Athanasius (Archbishop) on Sep 26, 2012 at 08:07 UTC
    (Replying to the Update in the OP.)
    The when ([wxID_YES]) {....} also works (why?)

    I also wondered that. :-) Here is the answer I came up with:

    1. From Experimental Details on given and when, it is clear that this is not one of the “10 exceptional cases” in which the expression is treated as a boolean. Therefore, smartmatching applies.

    2. From Smartmatch Operator:

      The smartmatch implicitly dereferences any non-blessed hash or array r +eference, so the HASH and ARRAY entries apply in those cases.
    3. Also from Smartmatch Operator:

      Right operand is an ARRAY: ... Any ARRAY smartmatch each ARRAY element like: grep { Any ~~ $_ } ARRAY
    4. and

      Num nummy numeric equality like: Num == nummy

      where nummy is defined as

      Either an actual number, or a string that looks like one.

    So,

    when ([wxID_YES])

    creates an anonymous array reference, and populates the array with the result of calling wxID_YES() — namely, 5103 — then it dereferences the array reference (1) & (2), and performs a smartmatch on the elements (3), effectively:

    grep { $selection ~~ $_ } (5103)

    which (4) reduces to:

    $selection == 5103

    which actually succeeds in being DWIM.

    All clear now?   ;-)

    *   *   *

    Also interesting is tobyink’s observation about constant, because

    use constant FOO => 42;

    is implemented as a subroutine named FOO. The significant point is that this subroutine is prototyped to take no arguments:

    sub FOO() { return 42; }

    which (together with some other requirements) allows Perl to inline it — that is, wherever FOO appears in the code (outside of quotes), it is replaced with the value 42, so smartmatching just works as expected. See Constant Functions.

    Athanasius <°(((><contra mundum

      Precisely.

      Smartmatch is easy. (Except when it isn't.)

      perl -E'sub Monkey::do{say$_,for@_,do{($monkey=[caller(0)]->[3])=~s{::}{ }and$monkey}}"Monkey say"->Monkey::do'

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://995476]
Approved by Athanasius
Front-paged by Old_Gray_Bear
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others having an uproarious good time at the Monastery: (6)
As of 2024-03-28 13:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found