Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Check if a subroutine is being used as an lvalue or an rvalue in Perl

I'm writing some code where I am using a subroutine as both an lvalue and an rvalue to read and write database values. The problem is, I want it to react differently based on whether it is being used as an lvalue or an rvalue.

I want the subroutine to write to the database when it is used as an lvalue, and read from the database when it is used as an rvalue.

Example:

# Write some data
$database->record_name($subscript) = $value;

# Read some data
my $value = $database->record_name($subscript);

The only way I can think of the make this work is to find a way for the subroutine to recognize whether it is being used as an lvalue or an rvalue and react differently for each case.

Is there a way to do this?

like image 317
tjwrona1992 Avatar asked Feb 03 '16 17:02

tjwrona1992


1 Answers

Deciding how to behave on whether it was called as an lvalue or not is a bad idea since foo(record_name(...)) would call it as an lvalue.

Instead, you should decide how to behave on whether it is used as an lvalue or not.

You can do that by returning a magical value.

use Variable::Magic qw( cast wizard );

my $wiz = wizard(
   data => sub { shift; \@_ },
   get => sub { my ($ref, $args) = @_; $$ref = get_record_name(@$args); },
   set => sub { my ($ref, $args) = @_; set_record_name(@$args, $$ref); },
);

sub record_name :lvalue {
   cast(my $rv, $wiz, @_);
   return $rv;
}

A little test:

use Data::Dumper;

sub get_record_name { print("get: @_\n"); return "val"; }
sub set_record_name { print("set: @_\n"); }

my $x = record_name("abc", "def");        # Called as rvalue

record_name("abc", "def") = "xyz";        # Called as lvalue. Used as lvalue.

my $y_ref = \record_name("abc", "def");   # Called as lvalue.
my $y = $$y_ref;                          #   Used as rvalue.
$$y_ref = "xyz";                          #   Used as lvalue.

Output:

get: abc def
set: abc def xyz
get: abc def
set: abc def xyz

After seeing this, you've surely learned that you should abandon the idea of using an lvalue sub. It's possible to hide all that complexity (such as by using sentinel), but the complexity remains. The fanciness is not worth all the complexity. Use separate setters and getters or use an accessor whose role is based on the number of parameters passed to it ($s=acc(); vs acc($s)) instead.

like image 86
ikegami Avatar answered Nov 18 '22 08:11

ikegami