Is there a problem with smart matching, when(), and &&?

brian_d_foy on 2008-02-18T06:19:39

I posted this to perl5porters without response, and also filed a bug report without a response. I figure it's either a bug no one has time to think about or I'm just an idiot (and I figure it might be a coin flip between those two). I'm trying to figure out when() using smart-matching combined with logical operators for the next edition of Learning Perl.

For a little background: perlsyn notes that there are exceptions to smart matching in when():

  • a subroutine call
  • a regex using $_ by default (which is a smart match, btw)
  • a regex expliclty bound to a variable (negated, even)
  • an explicit comparison
  • defined(), exists(), or eof()
  • a negated expression (!, not)
  • an xor

Right after that bit in perlsyn, it talks about &&.

If EXPR is ... && ... or ... and ... , the test is applied recursively to both arguments. If both arguments pass the test, then the argument is treated as boolean.

That last sentence is weird. If both arguments pass the test, it's treated as boolean. What does that imply if one or both don't pass the test? And, is "test" just "one of the previously listed exceptions".

So, the example I was playing with wanted to check if a value was in an array and also the key of a hash:

given( $foo ) {
	when( @n && %n ) { ... }
	}

Since neither of the arguments are one of the exceptions, I expected both arguments to be treated as a smart match:

given( $foo ) {
	when( $_ ~~ @n && $_ ~~ %n ) { ... }
	}

Now, nothing in the docs say that should be the case, so before I do too much work in going through the other odd situations I found, I figure I'll check if it's a doc problem first or if I'm being a lunkhead.

Here's what I think the docs are trying to say, and once we get this right (which might mean correcting my thinking and documenting it better) I can think about the rest of it:

If EXPR is ... && ... or ... and ..., and both of the arguments are one of the listed exceptions, Perl treats both arguments as booleans and performs no smart matching. If only one of the arguments is one of the exceptions, Perl treats that argument as a boolean and performs a smart match with the other argument. If neither argument is one of the exceptions, Perl performs a separate smart match with each argument.

However, I don't know how to square the statement with a test in t/op/switch.t. I think the test comment might be wrong. Should there be a smart match that fails in the first when(), which is why it moves onto the second? If so, the comment should say something like "((1 == 1) && \"bar\") used smart match and fails like it should".

# t/op/switch.t
{
	my $ok = 1;
	given("foo") {
	when((1 == 1) && "bar") {
		$ok = 0;
	}
	when((1 == 1) && $_ eq "foo") {
		$ok = 2;
	}
	}
	is($ok, 2, "((1 == 1) && \"bar\") not smartmatched");
}

I'll happily add some more tests once I know what the answers should be. :)

Here's a program I was playing with, and the output I got that led me to all of this.

		
use 5.010;

my @n = qw(0 Barney Wilma);
my %n = map { $_, 1 } @n;


$\ = "\n\t";

for( '', qw(0 1 Barney) )
	{
	my $n  = $_;

	say "\nProcessing [$n]...";
	
	when( %n  )      
		{ say "1. In \%n";   continue } # $_ ~~ %n
	when( @n  )      
		{ say "2. In \@n";   continue } # $_ ~~ @n
	when( @n && %n ) 
		{ say "3. @n && %n"           } # $_ ~~ @n && $_ ~~ %n ???
	}
	
__DATA__

Processing []...

Processing [0]...
1. In %n
2. In @n

Processing [1]...

Processing [Barney]...
1. In %n
2. In @n

Here's my machine and perl info:

macbookpro_brian[2845]$ perl5.10.0 -V
Summary of my perl5 (revision 5 version 10 subversion 0) configuration:
  Platform:
    osname=darwin, osvers=8.10.1, archname=darwin-2level
    uname='darwin alexandria2-10.nyc.access.net 8.10.1 darwin kernel version 8.10.1: wed may 23 16:33:00 pdt 2007; root:xnu-792.22.5~1release_i386 i386 i386 '
    config_args=''
    hint=recommended, useposix=true, d_sigaction=define
    useithreads=undef, usemultiplicity=undef
    useperlio=define, d_sfio=undef, uselargefiles=define, usesocks=undef
    use64bitint=undef, use64bitall=undef, uselongdouble=undef
    usemymalloc=n, bincompat5005=undef
  Compiler:
    cc='cc', ccflags ='-fno-common -DPERL_DARWIN -no-cpp-precomp -fno-strict-aliasing -pipe -I/usr/local/include -I/opt/local/include',
    optimize='-O3',
    cppflags='-no-cpp-precomp -fno-common -DPERL_DARWIN -no-cpp-precomp -fno-strict-aliasing -pipe -I/usr/local/include -I/opt/local/include'
    ccversion='', gccversion='4.0.1 (Apple Computer, Inc. build 5363)', gccosandvers=''
    intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
    d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
    ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
    alignbytes=8, prototype=define
  Linker and Libraries:
    ld='env MACOSX_DEPLOYMENT_TARGET=10.3 cc', ldflags =' -L/usr/local/lib -L/opt/local/lib'
    libpth=/usr/local/lib /opt/local/lib /usr/lib
    libs=-ldbm -ldl -lm -lc
    perllibs=-ldl -lm -lc
    libc=/usr/lib/libc.dylib, so=dylib, useshrplib=false, libperl=libperl.a
    gnulibc_version=''
  Dynamic Linking:
    dlsrc=dl_dlopen.xs, dlext=bundle, d_dlsymun=undef, ccdlflags=' '
    cccdlflags=' ', lddlflags=' -bundle -undefined dynamic_lookup -L/usr/local/lib -L/opt/local/lib'


Characteristics of this binary (from libperl): 
  Compile-time options: PERL_DONT_CREATE_GVSV PERL_MALLOC_WRAP
                        USE_LARGE_FILES USE_PERLIO
  Locally applied patches:
        RC2
  Built under darwin
  Compiled at Dec  2 2007 12:18:58
  @INC:
    /usr/local/perls/perl-5.10.0-rc2/lib/5.10.0/darwin-2level
    /usr/local/perls/perl-5.10.0-rc2/lib/5.10.0
    /usr/local/perls/perl-5.10.0-rc2/lib/site_perl/5.10.0/darwin-2level
    /usr/local/perls/perl-5.10.0-rc2/lib/site_perl/5.10.0
    .


It's broken

jjore on 2008-02-18T07:48:06

The actual code executing is pretty horrible.

( scalar( @n ) and scalar( %n ) )
  ~~ 'Barney'

Re:It's broken

jjore on 2008-02-18T08:25:42

That is:

( 3 and '2/8' ) ~~ 'Barney'

Perlmonks?

Ovid on 2008-02-18T18:57:29

I know the quality of answers can vary quite a bit, but Perlmonks still can be a good resource for answers.