http://qs321.pair.com?node_id=814557

JadeNB has asked for the wisdom of the Perl Monks concerning the following question:

The following amusing piece of code gives a reference to a length-2 array whose 0th and 1st entries may be changed, but are always equal to one another:

sub double { sub { \@_ }->($_[0], $_[0]) } my $r = double my $v; $v = 1; @$r; # => ( 1, 1 ); $v = 2; @$r; # => ( 2, 2 ); $r->[0] = 3; @$r; # => ( 3, 3 );

I would like to have a 3-argument subroutine switch_var such that the following code works:

switch_var $r, $v, my $w; $w = 4; @$r; # => ( 4, 4 );

Of course, a cheating way to do it is as follows:

sub switch_var { $_[0] = double $_[2] }
This satisfies the letter of my requirement, but not the spirit, because it only works for the particular $r I've given, not for an arbitrary circular reference.

Since the responses to Local for lexicals taught me the lesson of How to force one variable to be an alias to another?, I bethought myself of Data::Alias, but it doesn't work:

sub switch_var { alias $_[1] = $_[2]; } switch_var $r, $v, my $w; $w = 4; @$r; # => ( 3, 3 );
Even if I assume that I know something about the structure of the self-reference $r, I can only get at half of it:
sub switch_var { alias $_[0][0] = $_[2]; } switch_var $r, $v, my $w; $w = 4; @$r; # => ( 4, 3 )
I think that this is the behaviour mentioned in KNOWN ISSUES:
When aliasing existing lexical variables, the effect is limited in scope to the current subroutine and any closures create after the aliasing is done, even if the variable itself has wider scope. While partial fixes are possible, it cannot be fixed in any reliable or consistent way, and therefore I'm keeping the current behaviour.
Thus, I guess that what I'm specifically wondering here is: Does the behaviour that I want admit one of the partial fixes mentioned? If so, what is it?

Oh, and, since Local for lexicals also taught me that I should explain the ‘why’ as well as the ‘what’: What I'm trying to do is make fake subroutines for a combinator library I'm writing (sort of as a replacement for Sub::Compose, which is lovely but inherits the limitations of Data::Dumper::Streamer), so that I can write something like:

my $x; my $fake_sub = fake_subroutine $x => sub { \@_ }->($x, $x); my $r = call $fake_sub, my $y; $y = 1; @$r; # => ( 1, 1 );
The obvious response is “Why not use real subroutines?”, but it doesn't work for me: I'm using some trickery elsewhere so that I can tell the combinator SKK, say, to expect 1 argument $x, and it will figure out once and for all that it will just return $x (whatever it happens to be). The trickery I'm using (which is, literally, just calling my fake subroutine with a fresh variable $x and noting down the result as a ‘template’ for future calls) doesn't play well with real subroutines.

Replies are listed 'Best First'.
Re: Transitive aliases
by Anonymous Monk on Dec 28, 2009 at 01:45 UTC

    This may be missing the mark, because I don't understand your goal; but for the way switch_var is supposed to work, inside of that sub you could tie $w. Then setting it elsewhere in the program automatically sets $r. Based on my rememberance of subroutine prototypes, maybe something like:

    sub switch_var (\$$\$) { my ($reference, $initial, $binding) = @_; # bind $binding to scalar reference $reference tie $$binding, 'SomeClass', $reference; $$binding = $initial; }
    where SomeClass is a simple scalar tied package that implements at least TIE to store $reference and STORE to set @$reference = ($value) x $something.

      This is a good idea, although I think I may have made the problem statement over-specific—I'd really like to be able to handle any circular reference, not just a reference to an array of several copies of a single variable. Thus, maybe the appropriate thing to do would be

      sub switch_var { tie $_[2], to => $_[1]; } package to; sub TIESCALAR { bless \$_[1] => $_[0]; } sub STORE { ${$_[0]} = $_[1] } sub FETCH { ${$_[0]} }
      (There's some package out there already on CPAN that uses the to package for similar effect, but I can't find it. UPDATE: It's Tie::Util. Also, the functionality that I've described is quite similar—indeed, maybe identical—to that provided by Tie::Coupler.)

      I wonder if there's a non-tie-based solution? I might have to do switch_var several times, and going through many ties could get quite slow.