This and other RFCs are available on the web at
http://dev.perl.org/rfc/
=head1 TITLE
lvalue subs should receive the rvalue as an argument
=head1 VERSION
Maintainer: Andy Wardley <[EMAIL PROTECTED]>
Date: 15 Aug 2000
Version: 1
Mailing List: [EMAIL PROTECTED]
Number: 107
=head1 ABSTRACT
This RFC proposes that lvalue subs, when invoked as such, should receive
the rvalue being assigned to it as an argument.
=head1 DESCRIPTION
Take a simple object:
package Person;
sub new {
my ($class, $name) = @_;
bless { name => $name }, $class;
}
sub name {
my $self = shift;
if (@_) {
$self->{name} = shift;
print "changed name to $self->{name}\n";
}
return $self->{ name };
}
We define a simple accessor method, C<name>, to marshal access to the
internal name field. In this example it prints a message when the
name is changed but in the real world it might do something far more
important. The method can be used to get or set this field.
my $p = Person->new("Gandalf");
print "name: $p->name\n"; # "name: Gandalf"
$p->name("Mithrandir"); # "changed name to Mithrandir"
print "name: $p->name\n"; # "name: Mithrandir"
We can define this method as an C<lvalue> sub allowing it to be used
directly in assignment statements. Note that we must remove the
'return' in the last line of the method.
sub name : lvalue {
my $self = shift;
if (@_) { # Danger Will Robinson!
$self->{name} = shift;
print "changed name to $self->{name}\n";
}
$self->{name};
}
package main;
my $p = Person->new("Gandalf");
$p->name = "Mithrandir";
This "works" in the sense that the Person name is updated, but it
doesn't work the same as when called as a regular object method. The
reason is that lvalue calls don't pass the new value to the method.
Our check of C<@_> in the method returns false so the method doesn't
know that an attempt is being made to update the field. Instead it
blindy returns the internal field which Perl can then update directly,
using some kind of hidden magic and trickery.
$p->name('Gandalf'); # "changed name to Gandalf"
$p->name = 'Mithrandir'; # !! silence !!
print "name: $p->name\n"; # "name: Mithrandir"
The Person name is now set to 'Mithrandir', but managed to bypass the
marshalling code that we carefully constructed to trap any attempts to
update this field. This is inconsistent and allows the object
encapsulation to be violated.
It is proposed that lvalue subs should be called with the rvalue
passed as a regular argument. Thus,
$p->name = 'Mithrandir';
would be directly equivalent to
$p->name('Mithrandir');
This would allow accessor methods to detect, and if necessary,
intercept any attempts to update a particular field, even when
called as an lvalue sub.
We might want to consider a general rule stating that the rvalue is
added to the end of any existing argument list. This, it would be
possible to do this:
$obj->method($x, $y) = $z;
and have the method called as if written:
$obj->method($x, $y, $z);
This might make more sense when combined with named parameters.
$p->name(warnings => 0) = 'Mithrandir';
$p->name(%options) = $value;
equivalent to:
$p->name(warnings => 0, 'Mithrandir');
$p->name(%options, $value);
This may be a bad thing. The author makes no judgement either way.
=head1 IMPLEMENTATION
Hopefully trivial.
=head1 REFERENCES
None