Anonymous subroutine objects pattern |
In computer programming, the anonymous subroutine object pattern is one of the design pattern (computer science)s.
=Problem=
Perl5 s Object-oriented programming interface has many problems. Instance variables are slow to access, and require a special syntax that is unsightly and prevents easily converting procedural code to OO code. Subclass data can clobber superclass instance data unless manually prefixed with the class name. Or, you just want to integrate the Lambda programming style with the Object-oriented programming style to harness their respective strengths.
=Solution=
Mix Object-oriented and functional programming styles to deal with the ugliness of Perl s InstanceVariables syntax, write more concise program, and use scopes for implicit data flow rather than manually passing to and reading from constructors.
Lambda programming s concept of automatically binding code to a particular variable created at a particular time is the perfect replacement for using hashes or arrays to contain instance data. Instead, routines magically hang on to the normal scalars, hashes, arrays, and so forth that were defined with //my// when the object was created. All that is needed is a block to set up the lexical context (define the //my// variables in) and a little glue.
=Blessed Coderef=
One of the strengths of the LambdaProgramming style is ease of doing things like InnerClasses. Logic and data can be bundled without having to type out the name of each variable to pass it to a constructor, and without having to read it and assign it names in the constructor. Instead, the new object is automagically coupled to the object that it was created in, and variables that are in scope when the new object was created remain in scope and available for use in future calls.
You should be familiar with this by now:
package Preferences; sub new { my $class = shift; my %args = @_; bless {color=>$args{ color }, petname=>$args{ petname }, street=>{ street } }, $class; } sub query_color { return $_[0]->{ color }; } sub set_color { return $_[0]->{ color } = $_[1]; } # other accessors here 1; package main; $| = 1; print Whats your favorite color ; my $color = ; print Whats your pets name ; my $petname = ; print What street did you grow up on ; my $street = ; my $foo = new Preferences (color=>$color, petname=>$petname, street=>$street);
The string color appears ten times. Ten! In Perl, no less. If I wrote out the constructors for the other arguments, this would be repeated for each variable. Shame. If we trust the user to pass in the right things to the constructor, we can get rid of two. Still, even typing each thing eight times is begging for a typo to come rain on your parade.
If you re a LISP or Scheme programmer, you wouldn t even consider writing an autocracy like this. You d probably write something like:
package main; $| = 1; sub get_preferences { print Whats your favorite color ; my $color = ; print Whats your pets name ; my $petname = ; print What street did you grow up on ; my $street = ; return MessageMethod sub { my $arg = shift; ({ query_color => sub { return $color; } set_color => sub { $color = shift; return 1; } # etc }->{$arg} || sub { die Unknown request: $arg })->(@_); }; } my $ob = get_preferences(); print $ob->get_street(), ;
First, the //{ query_name => sub { } }->{$arg}->(@_)// is a sort of switch/case statement. It creates an anonymous hash of names to functions, then looks up one of the functions by name, using the first argument passed in. Once we have that code reference, we execute it and pass it our unused arguments. Then we ve added a default case to it, so we don t try to execute undef as code. This could have been coded using if/elsif/else just as easily.
Don t confuse the this case idiom //{ name => sub { } }->{$arg}->(@_)// with //=8>-()(@args)// syntax. //$ref->function(@args)// syntax is reserved for objects. We shouldn t be able to call $ob->get_street() in our example on a code reference -- unless that code reference has been blessed into a package. It just so happens that that is exactly what //MessageMethod// does.
package MessageMethod; sub new { my $type = shift; return $type->new(@_) if ref $type eq __PACKAGE__; my $ref = shift; ref $ref eq CODE or die; bless $ref, $type; } sub AUTOLOAD { my $me = shift; (my $method) = $AUTOLOAD =~ m/::(.*)$/; return undef if $method eq DESTROY ; return wantarray ($me->($method, @_)) : scalar $me->($method, @_); } 1;
Given a code reference, //MessageMethod// blesses it into its own package. There are no methods aside from //new()// and //AUTOLOAD()//. //AUTOLOAD()// handles undefined methods for Perl, and since there are no methods, it handles all of them. (There is an exception to that, where new() has to pass off requests). //AUTOLOAD()// merely takes the name of the function it is standing in for and sends that as the first argument to a call to the code reference, along with the rest of the arguments. We re translating //$ob->foo( bar )// into //$ob->( foo , bar )//. This does nothing but let us decorate our code reference with a nice OO style syntax.
This is similar to Python s method lookup logic XXX, in that it returns the method as an object.
=Blessed Hash full of Coderefs=
The previous example was simplicity itself. This one is usefulness itself. Doing //if// and //elsif// in a chain to inspect an argument to see which clause to run to simulate methods is the LambdaProgramming paradigm, but ObjectOriented s concept of automatically dispatching to methods is superior. Obviously, a single code reference isn t enough to let OO do its dispatch magic. We need something larger - something like a hashref that contains a bunch of coderefs, one coderef per method. The normal thing to do in Perl is to put all of the code directly in the package, using the symbol table (or stash, or namespace, or what have you) to hold all of the code references, and define the code references using a simple named //sub// statement. This doesn t allow each instance of the object to have different code references lexically bound to different InstanceVariables. We need private storage for the code references and the anonymous version of the //sub// statement. We need //hashclosure.pm//.
# place this code in hashclosure.pm # tell Perl how to find methods in this object - run the lambda closures the object contains sub AUTOLOAD { (my $method) = $AUTOLOAD =~ m/::(.*)$/; return if $method eq DESTROY ; our $this = shift; if(! exists $this->{$method}) { my $super = SUPER::$method ; return $this->$super(@_); } $this->{$method}->(@_); } 1;
This code translates method calls into invocations of anonymous subroutines by the same name inside of a blessed hash: when a method is called, we look for a hash element of that name, and if we find it, we execute it as a code reference.
XXX - diagram here.
Dropping the above code verbatim into a .pm file it doesn t change package (there is no //package// statement), so it defines an //AUTOLOAD()// method for the current package. This is a WrapperModule of sorts. LambdaClosures and our //AUTOLOAD()// method work together to provide ImplicitThis-like easy access to //$this// and InstanceVariables. We can use object instance specific field variables directly without having to dereference a hash.
package Foo; sub new { my $class = shift; my %args = @_; our $this; my $foo; my $bar; bless { get_foo => sub { return $foo }, set_foo => sub { $foo = shift }, get_bar => sub { return $bar }, set_bar => sub { $bar = shift }, get_foo_bar_qux => sub { return $this->get_foo(), $this->get_bar(), get_qux(); }, dump_args => sub { foreach my $i (keys %args) { print $i, = , $args{$i}, ; } }, }, $class; } sub get_qux { return 300; }
This blesses an anonymous hash reference into our package, //Foo//. This hash reference contains method names as keys and anonymous subroutines as values. //AUTOLOAD()// knows how to look into our hash and find methods by name, and run them, rather than looking for methods in the normal place.
//our// is a strange beast. It gives us a //my// style lexical alias to a //local// style variable. We could use a Subprogram#Local_variables, recursion and re-entrancy here, but //our// has a nicer syntax, and it keeps us in the lexical mode of thought.
//$foo//, //$bar//, //$this//, //$class// and //%args// are all lexical variables, and the subroutines we create with //sub { }// are LambdaClosures because they reference these variables. By referencing them, they bind to the one specific copy that was created when //new()// is entered. That means that each object has its own private //$foo//, for instance, and can access it directly. //get_qux()// is defined as a normal method in the preceding example. In any OO Perl code, failing to do something like //$this->method()// to call other functions in your code prevents inheritance from overriding those methods. Using this syntax keeps open the possibility of creating TemplateMethod. Where we explicit don t want subclass redefinitions of methods to be used, way can use the //$this->Foo::method()// syntax, where //Foo// is the name of the class to search for //method()// in, usually our own package or our direct parent.
Methods may also be defined normally and placed next to //new()//. This is useful for utility methods, or //static// methods in C++ or Java. Methods must be defined this way to be called without using the //$this->method()// syntax. //$this->method()// is required to get the //AUTOLOAD()// logic to kick in as otherwise Perl has no knowledge of how to locate the code responsible for handing your method.
This is my own personal favorite idiom for creating objects in Perl: it requires the least code to achieve, and the least work on my part, and the least chance of error.
In other news, PerlMonks:116725 defines a //class// package usable as such:
my $class = new class sub{ my $field = shift; $this->field = $field; $this->arrayref = [1,2,3]; $this->hashref = {a => b, c => d}; $this->method = sub{ return $this->field }; };
...allowing the anonymous, inline construction of classes.
=See also=
=External links=
[http://www.perldesignpatterns.com A Perl Design Patterns wiki]|
|