class Book
attr_reader :title
attr_accessor :price
def initialize(title, price)
@title, @price = title, price
end
end
or more verbosely in Ruby:
class Book
def initialize(title, price)
@title, @price = title, price
end
def title
@title
end
def price
@price
end
def price=(price)
@price = price
end
end
This is basically a case of price being a public property and title
being a protected one (i.e. only a read, no accessor).
So it seemed that with all the things Perl can do I should have been
able to do the same thing in Perl, right? Maybe with a little
finagling, but it hsould be possible.
I came up with this code:
package Book;
use strict;
our $AUTOLOAD;
sub new($$$) {
my $class = shift;
my $self = {};
$self->{_vars}->{protected} = [qw(title)];
$self->{_discard} = undef;
bless $self, ref $class || $class;
$self->{title} = shift;
$self->{price} = shift;
return $self;
}
sub AUTOLOAD : lvalue {
my $self = shift;
my $property = $AUTOLOAD;
$property =~ s/.*://;
if (exists $self->{$property}) {
if (grep $property eq $_, @{$self->{_vars}->{protected}}) {
warn "$property is Protected\n";
my $val = $self->{$property};
return $val;
}
$self->{$property};
}
else {
$self->{_discard};
}
}
Problem is, it doesn't work...
This code calling it dies at the second (price, the public one)
assignment attempt:
print "Title read: ", $book->title, "\n";
print "Price read: ", $book->price, "\n\n";
print "Title modify: ", $book->title = "Hello", "\n";
print "Price modify: ", $book->price = 5, "\n\n";
title is Protected
Title read: The Moon is a Harsh Mistress
Price read: 7.99
title is Protected
Title modify: Hello
Can't return a temporary from lvalue subroutine at lv.pl line 10.
Now, you aren't supposed to use return in an lvaluable subroutine, of
course... however, if I DON'T use a return, I get the error. And if I
do use return, the lvalue doesn't modify the actual thing.
Is there away around this? Can I make this work?
Granted, it's all for my own 'personal enrichment', not for any real
use or anything... still, I don't like the idea that I can't make
something work...
--
Sean 'Dodger' Cannon
BTW, I know of course I can easily do it without AUTOLOAD:
sub new($$$) {
my $class = shift;
my $self = {};
$self->{_vars}->{protected} = [qw(title)];
$self->{_discard} = undef;
bless $self, ref $class || $class;
$self->{title} = shift;
$self->{price} = shift;
return $self;
}
sub title {
shift->{title};
}
sub price : lvalue {
shift->{price};
}
That works fine. However, it seemed so strongly that I should be able
to do it with AUTOLOAD too...
--
Sean 'Dodger' Cannon
> So it seemed that with all the things Perl can do I should have been
> able to do the same thing in Perl, right? Maybe with a little
> finagling, but it hsould be possible.
Take a look at Contextual::Return. It may provide enough syntactical sugar
to make this easy.
Rough guess, untested:
sub _any(&@) { my $s = shift; $s->() && return 1 for @_; return; }
sub AUTOLOAD :lvalue {
my $self = shift;
my ($property) = $AUTOLOAD) =~ /::([^:]+)$/;
if (_any { $property eq $_ }, @{$self->{_vars}{protected}}) {
return RVALUE { $self->{$property} }
LVALUE { require Carp; croak "Can't use $property as lvalue" }
}
$self->{$property}
}
Or something like that.