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

Why does this first code work, and the latter doesn't? $asset->{related_files} gets filled from SQL with JSON content. then I do:
my $ref; if($asset->{related_files}){ $ref = decode_json($asset->{related_files}); }else{ $ref = []; }
which works. But I'd like to use the shorthand form, but it doesn't work! Seems the check for "if true" fails every time. It's not about the eval, isn't it? Where's the subtle difference? I've tried it with defined() and without.
## simpler shorthand-code doesn't work, why? eval { my $ref = defined($asset->{related_files}) ? decode_json($asset->{r +elated_files}) : []; }; if($@){ print "error" }

Replies are listed 'Best First'.
Re: I can't see why this shorthand doesn't behave like the verbose form
by SuicideJunkie (Vicar) on Mar 21, 2012 at 14:43 UTC

    How does it not work? Are you sure you want to declare the variable inside the eval rather than declaring it outside and then merely set it inside?

    Also, what makes you think that eval is shorter or simpler than the first code snippet? You're free to use the A?B:C in the first snippet too.

      O'Reilly's Advanced Perl Programming sais that eval'ed code is executed in the environment of the surrounding code, just as if the eval wasn't there - so declaring it inside or outside of the eval block is irrelevant
      Anyway, the eval just added noise to the core of my post, that's why I removed it from the follow up example.

        That does not change the scoping rules , {   } introduces a new scope

        $ perl -le " eval { my $foo = 1; } ; print $foo; " $ perl -le " use strict; eval { my $foo = 1; } ; print $foo; " Global symbol "$foo" requires explicit package name at -e line 1. Execution of -e aborted due to compilation errors.
Re: I can't see why this shorthand doesn't behave like the verbose form
by MidLifeXis (Monsignor) on Mar 21, 2012 at 15:23 UTC

    • Look at the scope of your my $ref variable.
      $ perl -e 'my $foo = "bar"; eval { my $foo = "biz" }; print $foo' bar
    • Is there a chance for anything inside of the eval block to die / throw an exception? If not, $@ will never be set. See die and perlvar.

    --MidLifeXis

Re: I can't see why this shorthand doesn't behave like the verbose form
by kcott (Archbishop) on Mar 22, 2012 at 07:01 UTC

    Firstly, I concur with comments above regarding scope and a lack of error description or output.

    My immediate thought was that you really want:

    my $ref = eval { defined($asset->{related_files}) ? decode_json($asset +->{related_files}) : []; };

    If the eval is failing, print out why, e.g. say $@ if $@; or something similar. Printing "error" is as useless as telling us "it didn't work".

    Decide whether your condition should test for truth, defined-ness or existence. See the first three example lines from exists:

    print "Exists\n" if exists $hash{$key}; print "Defined\n" if defined $hash{$key}; print "True\n" if $hash{$key};

    Try a much simpler form of the two constructs. Maybe something like:

    $ perl -Mstrict -Mwarnings -E 'my $x; eval { my $y = defined $x ? 1 : +0; }; say $@ if $@; say $y;' Global symbol "$y" requires explicit package name at -e line 1. Execution of -e aborted due to compilation errors. $ perl -Mstrict -Mwarnings -E 'my $x; my $y = eval { defined $x ? 1 : +0; }; say $@ if $@; say $y;' 0

    Although I can't see it being an issue here, autovivification could be a factor in this type of code and should at least be considered.

    If you still haven't tracked down the problem by this stage, now would be the time to start looking at SQL and JSON.

    -- Ken

Re: I can't see why this shorthand doesn't behave like the verbose form
by isync (Hermit) on Mar 25, 2012 at 11:53 UTC
    @Ken: I think you are right - it's time to look at JSON and SQL.

    Today I've found the time to hack together a simple test script that doesn't exhibit the original problem. Here, when the variable is properly declared (or not) and populated in perl context, shorthand and longform behave the same.

    Also, I've removed the noise of the eval and the defined() check, the latter I threw in anyway down the road when the simple check didn't seem to suffice.

    In my production code the related_files data comes from a SQL field that is parsed based on the test if($sql->{field}), so I think that there's a slight difference how perl regards this field as defined, existing or true. But I am not so inclined to find out why exactly the shorthand doesn't work. As the more verbose code just works. Also, might also be an unrelated error I am overseeing. I'll post if there's a find in the future.
    #! perl use Data::Dumper; use JSON::XS; my $asset = { ## rename this var to something else to test the 'undefined' c +ase related_files => '{ "firstName": "John", "lastName" : "Smith", "age" : 25, "address" : { "streetAddress": "21 2nd Street", "city" : "New York", "state" : "NY", "postalCode" : "10021" }, "phoneNumber": [ { "type" : "home", "number": "212 555-1234" }, { "type" : "fax", "number": "646 555-4567" } ] }' }; my $ref; if($asset->{related_files}){ $ref = decode_json($asset->{related_files}); }else{ $ref = []; } print Dumper($ref); ## simpler shorthand-code doesn't work, why? my $ref = $asset->{related_files} ? decode_json($asset->{related_files +}) : []; print Dumper($ref);

      Really? Your script works as expected on this machine. I suppose you might have a severely broken JSON::XS - that's the only place I can see it possibly breaking. Tried JSON::PP?

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

      Here is a demonstration of it working, and some alternatives :)

      #!/usr/bin/perl -- use strict; use warnings; use Data::Dump; use JSON::XS; my $ref = { foo => q{[1,2,3]} }; for my $key( qw/ bar foo /) { { my $foo; if( $ref->{$key} ){ $foo = decode_json( $ref->{$key} ); } else { $foo = []; } dd $foo; } { my $foo = []; if( $ref->{$key} ){ $foo = decode_json( $ref->{$key} ); } dd $foo; } { my $foo = $ref->{$key} ? decode_json( $ref->{$key} ) : []; dd $foo; } { my $foo = eval { decode_json( $ref->{$key} ) } || []; dd $foo; } } __END__ [] [] [] [] [1, 2, 3] [1, 2, 3] [1, 2, 3] [1, 2, 3]