Returning a lazily-computed scalar, in perl

I am trying to add some functionality to our code base using anchored scalars.

We have a function that is indicated for returning scalars. I thought I could add some functions to the system by tying these scalars before they return, but it looks like the FETCH method is called immediately before the return, which leads to the return of the unbound scalar.

Is there any way around this?

I really want to keep the routine interface (returning scalars) intact, if at all possible.

use strict; use warnings; main(); sub GetThing{ my $thing; tie $thing, 'mything', @_; return $thing; } sub main { my %m; $m{pre} = GetThing('Fred'); print "1\n"; print $m{pre}; print "2\n"; print $m{pre}; print "3\n"; } package mything; require Tie::Scalar; my @ISA = qw(Tie::StdScalar); sub TIESCALAR { my $class = shift; bless { name => shift || 'noname', }, $class; } sub FETCH { my $self = shift; print "ACCESS ALERT!\n"; return " NAME: '$self->{name}'\n"; } 

Required Conclusion:

 1 ACCESS ALERT! NAME: 'Fred' 2 ACCESS ALERT! NAME: 'Fred' 3 

I can get the desired result by returning the link and dereferencing on every access, but this destroys our installed interface and makes it more confusing for our users.

- Buck

+4
source share
4 answers

As DVK said, binding applies to containers, so it is not suitable for return values.

You use overload for this. Example (not all possible overloaded operations are given, see http://perldoc.perl.org/overload.html#Minimal-set-of-overloaded-operations ):

 use strict; use warnings; main(); sub GetThing{ my $thing; $thing = "mything"->new(@_); return $thing; } sub main { my %m; $m{pre} = GetThing('Fred'); print "1\n"; print $m{pre}; print "2\n"; print $m{pre}; print "3\n"; } package mything; use overload 'fallback' => 1, '""' => 'FETCH'; sub new { my $class = shift; bless { name => shift || 'noname', }, $class; } sub FETCH { my $self = shift; print "ACCESS ALERT!\n"; return " NAME: '$self->{name}'\n"; } 
+4
source

As mentioned in other answers, tie is applied to containers and not to values, so there is no way to assign a bound variable to another variable and save the associated properties.

Since there is no assignment, you need to pass the container to the GetThing routine. You can do this by reference as follows:

 use strict; use warnings; main(); sub GetThing{ tie ${$_[1]}, 'mything', $_[0]; } sub main { my %m; GetThing('Fred' => \$m{pre}); print "1\n"; print $m{pre}; print "2\n"; print $m{pre}; print "3\n"; } package mything; require Tie::Scalar; my @ISA = qw(Tie::StdScalar); sub TIESCALAR { my $class = shift; bless { name => shift || 'noname', }, $class; } sub FETCH { my $self = shift; print "ACCESS ALERT!\n"; return " NAME: '$self->{name}'\n"; } 

which produces the correct conclusion.

However, if you want to keep the assignment, you will need to use overloading, which applies to values ​​(actually to objects, but they themselves are values). Without detailed information on the purpose, it is difficult for you to give a complete answer, but this will meet your stated requirements:

 use strict; use warnings; main(); sub GetThing{ return mything->new( shift ); } sub main { my %m; $m{pre} = GetThing('Fred'); print "1\n"; print $m{pre}; print "2\n"; print $m{pre}; print "3\n"; } package mything; sub new { my $class = shift; bless { name => shift || 'noname', }, $class; } use overload '""' => sub { # '""' means to overload stringification my $self = shift; print "ACCESS ALERT!\n"; return " NAME: '$self->{name}'\n"; }; 

Both connections and overloads can get complicated, so read through all the documentation if something is unclear.

+3
source

The first , exact method to do what you offer seems technically impossible:

  • Bind variables tied to the variable itself, and not to its value.

  • In Perl, the return values ​​of a subroutine are returned by value , which means that you accept the value passed to return , refer to it (in this case, access the associated variable and call FETCH in the process) - and then copy that value ! This means that what the caller receives is a VALUE scalar value, not a scalar variable (bound or decoupled).

Your confusion, in short, is related to mixing variables (locations in the program symbol table) and values ​​stored in these variables.


Second , you were somewhat unclear as to what exactly you are trying to achieve , so it’s hard to suggest how to achieve what you want. But assuming, based on your description, that you want to call a method when returning a subroutine (possibly passing it a return value), you can do it.

To do this, you need to use what fancy people call aspect programming . Politically (and technically) the right way to do this in Perl is to use Moose.

However, you can do this by replacing the original method with a wrapper method.

The exact mechanics of both Moose and DIY approaches can be seen in the first two answers to the next SO question, so I will not copy / paste them here, I hope you do not mind:

Simulation of aspects of static typing in duck language

+2
source

If you're feeling adventurous, you can also use the Scalar :: Defer module , which provides a universal mechanism for a scalar variable to calculate the value lazily, either once or every access.

0
source

All Articles