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

I am writing code where I use a routine of both lvalue and rvalue to read and write database values. The problem is that I want it to react differently depending on whether it is used as an lvalue or rvalue.

I want the routine 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 doing this work is to find a way for the subroutine to recognize if it is used as an lvalue or rvalue and to respond differently for each case.

Is there any way to do this?

+8
subroutine perl rvalue lvalue
source share
3 answers

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

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

You can do this by returning magical meaning .

 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 small 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 

Seeing this, you, of course, have learned that you should abandon the idea of โ€‹โ€‹using lvalue sub. You can hide all this complexity (for example, using sentinel ), but the complexity remains. Whimsicality is not worth all the complexity. Use separate setters and getters or use an accessory whose role is based on the number of parameters passed to it ( $s=acc(); vs acc($s) ).

+4
source share

In this situation, you can try my Sentinel module.

It provides a function that you can use in an accessory to turn it into a more attractive style. For example. you could

 use Sentinel qw( sentinel ); sub get_record_name { ... } sub set_record_name { ... } sub record_name { sentinel get => \&get_record_name, set => \&set_record_name, obj => shift; } 

At this point, the following pairs of lines of code are equivalent

 $name = $record->record_name; $name = $record->get_record_name; $record->record_name = $new_name; $record->set_record_name( $new_name ); 

Of course, if you donโ€™t need to provide specific versions of the get_ and set_ with prefixes, you can also embed them as closures.

See also module docs for further ideas.

+4
source share

In my opinion, Perl's lvalue routines were a dumb idea. Just maintain ->record_name($subscript, $value) as the setter and ->record_name($subscript) as the recipient.

However, you can use the Want module, like this

 use Want; sub record_name:lvalue { if ( want('LVALUE') ) { ... } else { ... } } 

although this also applies to LVALUE:

 foo( $database->record_name($subscript) ); 

If you want only assignment statements to be specially processed, use want('ASSIGN') instead.

0
source share

All Articles