Thursday, August 18, 2011

Why not bless objects into … metaobjects ?

Yesterday, Jesse Vincent gave a talk at YAPC::EU::2011 about the future of Perl; among many interesting things, he mentioned the idea of adding support for a metaobject protocol , directly in Perl5 core.

This immediately got me thinking, because in the past weeks I have been struggling with a design problem that is closely related to that topic.

My project was to add a layer of meta-information in version 2 of DBIx::DataModel, so that the client code could navigate through the available tables, joins, etc. available within the current datamodel. Since tables and joins are classes, it would seem natural to use class methods for navigation in the metamodel. However, in Perl there are no separate namespaces for class methods and instance methods : any subroutine declared in a package can be called either way; it's the code in the subroutine that has to inspect its first argument to know if it was called as a class or as an instance, and then take “the appropriate action”. So in short, the problem is :

  • we want each table to have an C method;
  • a table must be a class (so that rows from that table can be blessed into something)
  • a call to $row->associations() makes no sense, so there is no “appropriate action” (except throwing an exception, which is only halfway satisfactory)

The design decision for DBIx::DataModel was to create a separate set of metaobjects. When a Table() is defined, it simultaneously creates a subclass of DBIx::DataModel::Source::Table, and an instance of DBIx::DataModel::Meta::Source::Table. The class has a method to reach its metaobject, and the metaobject has an attribute to know which class it is attached to. Now the associations() method is defined in the metaobject, not in the subclass, so rows are not affected. It works, it's pretty clean in the separation of concerns, so I'm almost happy.

What would be really, really great, however, would be to be able to bless an object directly into its metaobject. Unfortunately, Perl won't let you do that : it raises an exception “Attempt to bless into a reference”. This makes sense, because Perl needs to know the name of the symbol table where it will start looking for methods. So in order to fool the compiler, let's put in a stringification operator :


package Meta;
use overload '""' => sub {my $self = shift; join "::", @$self};
sub meta_method {print "I'm a clever meta-method for $_[0]\n"}

package Foo::Bar;
sub m {print "hello from Foo::Bar\n"}

package Bar::Foo;
sub m {print "hello from Bar::Foo\n"}

package main;
my $FB = bless [qw/Foo Bar/], 'Meta';
my $BF = bless [qw/Bar Foo/], 'Meta';
my $fb = bless {}, $FB;
my $bf = bless {}, $BF;
$fb->m; $bf->m;

 

It works! This code compiles and runs! But it doesn't bring us very far, because stringification occurs at blessing time, which is too early : ref $fb returns the string 'Foo::Bar', not the metaobject; so it is not possible to call


(ref $fb)->meta_method

 


This would require a change in the perl5 compiler, allowing bless to take objects as second argument, instead of just strings. Conceptually this does not seem to be infeasible; it's quite similar to what happened a couple of years ago with exceptions (initially exceptions were just strings; now they can be objects too). And if we have that, we can build a metaobject protocol, we can build delegation chains à la Self or Smalltalk, etc.

Unfortunately, when I came to this point yesterday, it was too late to grab one of the perl5 core guys to discuss that idea. So let's do it online : what do you think of this proposal ?

3 comments:

  1. People are working on that sort of stuff (though it will probably look rather different from what you're suggesting). Let's hope it will manage to make it into 5.16.

    ReplyDelete
  2. Thanks for the comment ... I would appreciate to hear more about what those plans. Do you know if there is any online place where this project is being discussed ?

    ReplyDelete
  3. I wrote a prototype system called MRO::Magic to allow instances to be of their own type, referring to class instances -- basically class metaobjects. I've implemented several other OO systems using this, and have implemented MRO::Magic in a few ways, including blessing into anonymous stashes created by Florian Ragwitz's highly experimental Object::Anon. This is all wildly unstable code, but it shows the value of doing this sort of thing.

    Right now Stevan Little, Reini Urban, and some others are in discussion about how to build a very simple perl 5 MOP -- but this is mostly in preliminary design stages, and hasn't yet been presented publicly beyond notes in a git repository somewhere, probably under github.com/stevan

    ReplyDelete