Beefy Boxes and Bandwidth Generously Provided by pair Networks
XP is just a number

lvalue subs return undef, playing with experimental features, the End of the World, etc

by erikharrison (Deacon)
on May 07, 2002 at 05:06 UTC ( #164527=perlquestion: print w/replies, xml ) Need Help??
erikharrison has asked for the wisdom of the Perl Monks concerning the following question:

Some of you may remember that I (like Mr. Cawley) am implementing a programing language in Perl (though mine is just a toy). I finally got down to working on the code to implement variables, and happened upon attributes in the docs. Useing lvalue subs I was able to make a delightfully simple API for handling complex data structures with arrays, scalars, and hashes, alng with namespaces and lexical variables. The only problem is that it doesn't work.

use strict; use warnings; { my %hash; sub assign_to_hash : lvalue { my $key = shift; return $hash{ $key }; } } print "Couldn't set value!" unless assign_to_hash('foo') = (['array', +'of', 'values']); print "Value defined" if defined assign_to_hash('foo');

This gives no output.

This reduces the problem to the basics. Essentially, the lvalue sub does the hard part (be assigned to) but otherwise returns undef, as if the hash key is autovivifiying . . .which it shouldn't be, unless I'm badly mistaken (which I could be :-).

I understand that attributes are an experimental don't-use-this-even-if-your-life-depended-on-it feature (which is maturing nicely in Perl 6) but I'm still curious if anyone can explain it.

Cheers, (and thanks)
  • Comment on lvalue subs return undef, playing with experimental features, the End of the World, etc
  • Download Code

Replies are listed 'Best First'.
Re: lvalue subs return undef, playing with experimental features, the End of the World, etc
by tadman (Prior) on May 07, 2002 at 06:08 UTC
    As far as I know, when using lvalue subs you are not supposed to explicitly return anything. You just have to gently place the variable you want at the appropriate position and pray to the Perl gods to take it away. If you return it, the whole contract breaks down. Even an if can ruin it, as your variable is stuck in the wrong spot.
    sub assign_to_hash : lvalue { exists($hash{$_[0]})? $hash{$_[0]} : undef; }
    That should certainly do the trick. If you don't care about assigning to new places, you can simplify:
    sub assign_to_hash : lvalue { exists($hash{$_[0]})? $hash{$_[0]} : $hash{$_[0]} = undef; }
    It's peculiar, but effective. Just be careful.

      *sigh* You indeed seem to be correct. Removing the offendinf return solves the beast . . . unfortunately it means that lvalue subs probably will not solve my problem (unless I jump through alot of hoops to guarentee a single exit point for the sub . . .hmmmm . . . )

      tadman++ (as soon as votes recycle)


      ps - Where is this documented? I thought I read the docs pretty thoroughly on this, but I could be embaressingly mistaken.

        If you check the perlsub section of the manuals, you get a very brief introduction to the "lvalue" feature. Here is the example:
        my $val; sub canmod : lvalue { $val; } sub nomod { $val; }
        This isn't terribly helpful, is it? Yet it does illustrate how you are just supposed to leave it there. Perl subroutines normally work such that the last thing left on the stack gets returned, but in the case of an lvalue-enabled subroutine, there must be a special handler that converts the stack entry into a kind of reference which can be assigned to.

        Try colapsing your logic into an unfortunately ugly ?: chain:
        my %foo; my $bar; sub foo : lvalue { my ($key) = @_; if (some_complex_condition()) { do_some_stuff($foo{a}); $key = "b"; } if (other_complex_condition()) { $foo{a} .= do_some_other_stuff($foo{b},$foo{c}); $key = "a"; } defined($key)? exists($foo{$key})? $foo{$key} : $foo{$key} = undef : $bar; }
        First, figure out what you need to return, taking as much time as is necessary. Then, once you know, shuffle the appropriate variable to the top of the stack and leave it there.
Re: lvalue subs return undef, playing with experimental features, the End of the World, etc
by Zaxo (Archbishop) on May 07, 2002 at 05:53 UTC

    Seems to work here, what version of perl are you using and how are you calling the sub? Here's a version abridged for the command line:

    $ perl -Mstrict -we'{my %hsh;sub set:lvalue{$hsh{shift}}}my $baz=set(" +foo")="bar"; print $baz,$/,set("foo"),$/' bar bar $
    This is Perl 5.6.1.

    Update: Changing the oneliner to be more like yours, I get:

    $ perl -Mstrict -we'{my %hsh;sub set:lvalue{$hsh{shift}}}my $baz=set(" +foo")=([qw/bar baz/]); print $baz,$/,defined set("foo"),$/' ARRAY(0x8106284) 1 $
    which is what I'd expect. Recent AS should be fine.

    U 2 : ++tadman for the solution, well spotted!

    After Compline,

      Most recent Active State build on Windows 98.

      I'm running the code exactly as it is in the node - it is called in the last two lines.

      Indeed, your oneliner version runs fine (though I though that the $hsh{shift} needed to be $hsh{+shift}. What am I missing?)


Log In?

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

How do I use this? | Other CB clients
Other Users?
Others about the Monastery: (4)
As of 2017-10-23 00:55 GMT
Find Nodes?
    Voting Booth?
    My fridge is mostly full of:

    Results (275 votes). Check out past polls.