Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl: the Markov chain saw

converting from switch to given-when

by jmlynesjr (Hermit)
on Sep 25, 2012 at 01:29 UTC ( #995476=perlquestion: print w/replies, xml ) Need Help??
jmlynesjr has asked for the wisdom of the Perl Monks concerning the following question:


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) {....}

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.


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 (Chancellor) 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 (Abbot) 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 (Chancellor) 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.


    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


      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?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://995476]
Approved by Athanasius
Front-paged by Old_Gray_Bear
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others browsing the Monastery: (2)
As of 2018-05-23 04:05 GMT
Find Nodes?
    Voting Booth?