perl-diddler has asked for the wisdom of the Perl Monks concerning the following question:

If I have a sub that can return a value, I can have it return nothing, a SCALAR or an ARRAY based on wantarray.

What I'm wondering is if the sub is an 'lvalue' sub, can I return something different at the end based on whether or not the sub is called in an lvalue-wanting context vs. an rvalue-wanting context?

I.e. is there something like wantlvalue that would return '1' if sub is called in an lvalue context, '0' if in an 'rvalue' context, or 'undef' if called in a null context?

Tnx!

Replies are listed 'Best First'.
Re: can sub check context for lvalue vs rvalue context?
by LanX (Saint) on May 08, 2018 at 23:11 UTC
    I don't think so, that's why you are not recommended to use lvalue methods as combined getter and setters.

    It initially works but if you ever need to do validation on the setter you are lost.

    use strict; use warnings; package Test; my $test; sub test :lvalue { return $test; } package main; Test->test =5; print Test->test; #> 5

    Though there was a costly workaround by returning a tied value (from TheDamian IIRC) which catches the STORE.

    update

    Well, if you are willing to install XS, you could use Devel::Callsite to find and parse the OP-Tree.

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Wikisyntax for the Monastery

      Yeah, returning a tied value is what I was grudgingly working on, as I didn't think it would be very efficient.

      I was hoping I could return a non-tied value for rvalue context as 'reading' a var is often a more frequent operation than 'writing' to it.

      I've used lvalues for many functions, where I don't need to check the value, but do want assignments to structures to be checked for valid member names. I use structures more often than objects, which is why I shook my head at the discouragement against lvalue accessor subs.

      Of course if you want to do something contingent on value change...then it seems perl only offers a tied-type solution.

      It's too bad a "wantlvalue" keyword wasn't introduced with lvalue subs -- it would have been at least as useful as 'wantarray'. Imagine all the dual-use functions that return different values based on scalar or array context that would not be do-able w/o knowing return context. It seems that's an equivalent case to knowing whether or not the return context is lvalue or not.

        Of course if you want to do something contingent on value change...then it seems perl only offers a tied-type solution.

        The documentation Lvalue subroutines does make a reference to Sentinel, which, from a quick look at the docs, appears to implement something like a tied scalar, in XS.

        The tied scalar solution appears to already have been implemented with Sub::Lvalue, and in fact a search for "lvalue" on CPAN will show that the wheel has been reinvented several times, including making object accessors lvalues. One of the most comprehensive ones appears to be Contextual::Return (which implements its LVALUE/RVALUE blocks using tied scalars, too).

        Yeah :lvalue is kind of orphaned and I rarely see it in use.

        An implementation by scanning the IP tree shouldn't be too difficult, so maybe habe look into CPAN.

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Wikisyntax for the Monastery

        > Yeah, returning a tied value is what I was grudgingly working on, as I didn't think it would be very efficient

        There is probably a solution without XS ...

        You could compare the caller in the tied STORE and set a flag $islvalue for the next call.

        Like that only your first call would be inefficient.

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Wikisyntax for the Monastery

Re: can sub check context for lvalue vs rvalue context?
by BrowserUk (Patriarch) on May 08, 2018 at 22:23 UTC

    What would your wantlvalue return if called this way:  @x = x() = 5;?


    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority". The enemy of (IT) success is complexity.
    In the absence of evidence, opinion is indistinguishable from prejudice. Suck that fhit
      What would your wantlvalue return if called this way: @x = x() = 5;?

      I think operator associativity can help answer that one:

      $ perl -MO=Deparse -e ' @x = x() = 5; ' @x = (x() = 5);

      So I'd say that x() is primarily in lvalue context here. And what's being assigned to @x is not the return value of x(), but the return value of the assignment operation, which is an lvalue itself.

      $ perl -le 'sub x : lvalue { $a } (x() = 4) = 2; print $a' 2
        So I'd say that x() is primarily in lvalue context here.

        That implies it has a secondary context. It doesn't. When an lvalue sub is called, it always does the same thing. Returns an lvalue to the calling context.

        And what's being assigned to @x is not the return value of x(), but the return value of the assignment operation, which is an lvalue itself.

        What is assigned to @x, is the result of the rvalue expression. And that, is the result of the assignment to lvalue returned by the subroutine.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority". The enemy of (IT) success is complexity.
        In the absence of evidence, opinion is indistinguishable from prejudice. Suck that fhit
      What happens with Tie? Does it call a separate Store then Fetch?

      Either way, a new value has to be passed into 'X', so it has to be called first in an lvalue context. Then it depends on whether or not perl makes a separate call to the function to do the equivalent of a fetch operation.

      If it does, the 2nd call would call it in rvalue context, but I'd think the 2nd call wouldn't happen, as perl would already have the value it stored and not need to make a 2nd call.

        Well, its easy to demonstrate there is only one call made. So what now?

        I think the easiest way to think of it, is that the function returns an lvalue to the calling context; and has no say or knowledge of how that lvalue is used there.

        And in reality, that is exactly what happens:

        { my $X = 12345; sub x :lvalue { $X } };; print x();; 12345 $r = \x();; print x();; 12345 $$r = 456;; print x();; 456

        Think of a function returning a reference. It does not know and cannot influence whether that reference will be used to read the referent value, or assign a new value to it.

        This is analogous; thus even if you knew what the context was, it would not benefit you to know. (Also why :lvalue never made it off the 'experimental' list.)


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority". The enemy of (IT) success is complexity.
        In the absence of evidence, opinion is indistinguishable from prejudice. Suck that fhit
Re: can sub check context for lvalue vs rvalue context?
by ikegami (Patriarch) on May 11, 2018 at 20:04 UTC

    It's not clear what you are asking, but the comments make me think the goal is to avoid returning expensive tied variables when not necessary.

    This is what substr, keys, vec and $#a do. The catch is these are ops, so they can simply do

    I32 lvalue = PL_op->op_flags & OPf_MOD || LVRET;

    For a sub, you'd need to locate the op that called the sub first. Contextual::Return can do this for you.

    sub foo :lvalue { LVALUE { ... } RVALUE { ... } }

    But does this actually speed things up? That's for you to find out.


    By the way, there are lighter types of magical variables than tied variables that can be used here.

    use Variable::Magic qw( wizard cast ); { my $wiz = wizard( # Called before reading the magic scalar (${$_[0]}). # Assign the value to ${$_[0]} that you want to be fetched. # $data is $_[1]. get => sub { ... }, # Called after an assignment to the magic scalar (${$_[0]}). # $data is $_[1]. set => sub { ... }, ); sub foo :lvalue { LVALUE { my $data = ...; cast(my $magical, $wiz, $data); $magical } RVALUE { ... } } }
      Variable::Magic -- thought it might use a form of tied underneath, but it's really other way around and looks like it might be more efficient, so will maybe poke around w/it for a bit...

      thanks!

Re: can sub check context for lvalue vs rvalue context?
by ikegami (Patriarch) on May 11, 2018 at 01:32 UTC

    An :lvalue sub always returns an a lvalue, whether its called in lvalue context or not.

Re: can sub check context for lvalue vs rvalue context?
by Anonymous Monk on May 11, 2018 at 18:36 UTC

    There does not seem to be a meaningful distinction in Perl. The return value is not intimately connected to the following assignment. Consider this snippet:

    use Data::Dump; my $foo = 7; sub bar :lvalue { $foo } sub baz :lvalue { bar } dd $foo, bar(), baz(); baz = 9; for (baz) { dd $foo; $_ = 4; dd $foo; }

    As you can see, $foo is passed around (by reference), but there is no telling if and where it is going to be assigned.