Use would be similar to before:
use Person; $him = Person->new(); $him->name("Jason"); $him->age(23); $him->peers( [ "Norbert", "Rhys", "Phineas" ] ); printf "%s is %d years old.\n", $him->name, $him->age; print "His peers are: ", join(", ", @{$him->peers}), "\n";
but the implementation would be radically, perhaps even sublimely different:
package Person;
sub new { my $that = shift; my $class = ref($that) || $that; my $self = { NAME => undef, AGE => undef, PEERS => [], }; my $closure = sub { my $field = shift; if (@_) { $self->{$field} = shift } return $self->{$field}; }; bless($closure, $class); return $closure; }
sub name { &{ $_[0] }("NAME", @_[ 1 .. $#_ ] ) } sub age { &{ $_[0] }("AGE", @_[ 1 .. $#_ ] ) } sub peers { &{ $_[0] }("PEERS", @_[ 1 .. $#_ ] ) }
1;
Because this object is hidden behind a code reference, it's probably a bit
mysterious to those whose background is more firmly rooted in standard
procedural or object-based programming languages than in functional
programming languages whence closures derive. The object created and
returned by the new
method is itself not a data reference as
we've seen before. It's an anonymous code reference that has within it
access to a specific version (lexical binding and instantiation) of the
object's data, which are stored in the private variable $self. Although
this is the same function each time, it contains a different version of
$self.
When a method like $him->name
is called, its implicit zeroth argument is the invoking object--just as it is with all method calls. But in this case, it's our code reference (something like a function pointer in
C++, but with deep binding of lexical variables). There's not a lot to be done with a code reference beyond calling it, so that's just what we do when we say
&{$_[0]}
. This is just a regular function call, not a method call. The initial argument is the string
``NAME'', and any remaining arguments are whatever had been passed to the method itself.
Once we're executing inside the closure that had been created in new,
the $self
hash reference suddenly becomes visible. The closure grabs its first argument
(``NAME'' in this case because that's what the name
method passed it), and uses that string to subscript into the private hash hidden in its unique version of $self.
Nothing under the sun will allow anyone outside the executing method to be able to get at this hidden data. Well, nearly nothing. You could single step through the program using the debugger and find out the pieces while you're in the method, but everyone else is out of luck.
There, if that doesn't excite the Scheme folks, then I just don't know what will. Translation of this technique into C++, Java, or any other braindead-static language is left as a futile exercise for aficionados of those camps.
You could even add a bit of nosiness via the caller
function
and make the closure refuse to operate unless called via its own package.
This would no doubt satisfy certain fastidious concerns of programming
police and related puritans.
If you were wondering when Hubris, the third principle virtue of a programmer, would come into play, here you have it. (More seriously, Hubris is just the pride in craftsmanship that comes from having written a sound bit of well-designed code.)