autobox::Closure::Attributes - closures are objects are closures
Version 0.03 released 16 May 08
use autobox::Closure::Attributes; sub accgen { my $n = shift; return sub { $n += shift || 1 } } my $from_3 = accgen(3); $from_3->n # 3 $from_3->() # 4 $from_3->n # 4 $from_3->n(10) # 10 $from_3->() # 11 $from_3->m # "CODE(0xDEADBEEF) does not close over $m"
The venerable master Qc Na was walking with his student, Anton. Hoping to prompt the master into a discussion, Anton said "Master, I have heard that objects are a very good thing - is this true?" Qc Na looked pityingly at his student and replied, "Foolish pupil -- objects are merely a poor man's closures."
Chastised, Anton took his leave from his master and returned to his cell, intent on studying closures. He carefully read the entire "Lambda: The Ultimate..." series of papers and its cousins, and implemented a small Scheme interpreter with a closure-based object system. He learned much, and looked forward to informing his master of his progress.
On his next walk with Qc Na, Anton attempted to impress his master by saying "Master, I have diligently studied the matter, and now understand that objects are truly a poor man's closures." Qc Na responded by hitting Anton with his stick, saying "When will you learn? Closures are a poor man's objects." At that moment, Anton became enlightened.
This module uses powerful tools to give your closures accessors for each of the closed-over variables. You can get and set them.
You can also call invoke methods defined in the package the coderef was created in:
{ package Foo; sub new { my $package = shift; my $x = shift; sub { $x *= 2; }; } sub inc_x { my $self = shift; ++ $self->x } }; my $foo = Foo->new(10); $foo->inc_x; # $x is now 11; calls the method inc_x $foo->x = 15; # 15; assigns to $x $foo->x(20); # 20; assigns to $x $foo->(); # 40; runs the sub { } $foo->m; # "CODE(0xDEADBEEF) does not close over $m"
Note that the coderef returned by sub { } was never blessed.
sub { }
bless
If Foo is used from a different file with use, then you'll need this boilerplate in your Foo.pm:
Foo
use
Foo.pm
sub import { my $class = shift; $class->autobox::import(CODE => 'autobox::Closure::Attributes::Methods'); }
That enables autoboxing of code references in the program that uses the Foo.pm module.
You can get and set arrays and hashes too, though it's a little more annoying:
my $code = do { my ($scalar, @array, %hash); sub { return ($scalar, @array, %hash) } }; $code->scalar # works as normal my $array_method = '@array'; $code->$array_method(1, 2, 3); # set @array to (1, 2, 3) $code->$array_method; # [1, 2, 3] my $hash_method = '%hash'; $code->$hash_method(foo => 1, bar => 2); # set %hash to (foo => 1, bar => 2) $code->$hash_method; # { foo => 1, bar => 2 }
If you're feeling particularly obtuse, you could do these more concisely:
$code->${\ '%hash' }(foo => 1, bar => 2); $code->${\ '@array' }
I recommend instead keeping your hashes and arrays in scalar variables if possible.
The effect of autobox is lexical, so you can localize the nastiness to a particular section of code -- these mysterious closu-jects will revert to their inert state after autobox's scope ends.
Go ahead and read the source code of this, it's not very long.
autobox lets you call methods on coderefs (or any other scalar).
PadWalker will let you see and change the closed-over variables of a coderef .
AUTOLOAD is really just an accessor. It's just harder to manipulate the "attributes" of a closure-based object than it is for hash-based objects.
<#moose:jrockway> that reminds me of another thing that might be insteresting: <#moose:jrockway> sub foo { my $hello = 123; sub { $hello = $_[0] } }; my $closure = foo(); $closure->hello # 123 <#moose:jrockway> basically adding accessors to closures <#moose:jrockway> very "closures are just classes" or "classes are just closures"
Shawn M Moore, sartak@gmail.com
sartak@gmail.com
autobox, PadWalker
The "WHAT?" section is from Anton van Straaten: http://people.csail.mit.edu/gregs/ll1-discuss-archive-html/msg03277.html
my $code = do { my ($x, $y); sub { $y } }; $code->y # ok $code->x # CODE(0xDEADBEEF) does not close over $x
This happens because Perl optimizes away the capturing of unused variables.
Perl 5.8.9, 5.10.0, 5.12.0, and other earlier versions fail on the package method examples with the error: Can't modify non-lvalue subroutine call at /home/knoppix/lib/perl5/site_perl/5.8.9/autobox/Closure/Attributes.pm line 37. Change the mutators to instead read:
sub inc_x :lvalue { ++ $_[0]->x; $_[0]->x } sub inc_y :lvalue { ++ $_[0]->y; $_[0]->y }
5.14 onward are smart enough to know that those accessors aren't actually called in lvalue context even though they're called from an lvalue method (AUTOLOAD).
AUTOLOAD
Copyright 2007-2009 Shawn M Moore.
Copyright 2013 Scott Walters (scrottie).
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
To install autobox::Closure::Attributes, copy and paste the appropriate command in to your terminal.
cpanm
cpanm autobox::Closure::Attributes
CPAN shell
perl -MCPAN -e shell install autobox::Closure::Attributes
For more information on module installation, please visit the detailed CPAN module installation guide.