The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

Create Your Own Object Model

So all this meta-meta-stuff can be interesting, but the real question is: How does it fit into a real-world scenario? I mean, what do you do with all this abstraction?

Why make your own object model of course :)

Designing your object model

First we need to decide what to put into our object model. Now since this is an example for illustrative purposes, I will try to keep it simple. However, I think you will be surprised to see how easy it is to make robust object model using meta-classes.

So obviously we will have classes in our model. For the purposes of our example, we will define a class as simply a collection of properties and methods, and we won't bother about public/private or any such things.

We will also add abstract classes to our model. We will define an abstract class as being a class with no instance variables, and whose method labels are defined, but are not implemented. This is a very simplistic definition of abstract classes, but it should suffice for this example.

The last item we will add to our object model are thread safe classes. These are classes which will be guaranteed to be thread safe. And despite what you might think, this one is actually very simple to implement.

Now with our object model defined, let's build it.

Building your object model

First we determine the structure of our meta-class hierarchy. In my mind, abstract classes should be our base meta-class; with regular classes being a subclass of abstract classes; and finally the thread safe classes being a subclass of our classes.

First we define our AbstractClass meta-class:

 {
    'class' => MetaClass,
    MetaClass => {
        'name' => 'AbstractClass',
        'parent' => undef,
        'instance_vars' => {
            AbstractClass => { 
                'name'          => "",
                'parents'       => [],
                'method_table'  => {}
            }
        },
        'method_table' => {
            'create_instance' => { 
                die "You cannot instantiate an AbstractClass";
            },
            'add_method_to_class' => ($class, $label) -> {
                $class.<method_table>.<$label> = \&abstractMethodHandler;
            },
            ...
        }
    }
 }

An AbstractClass does not have a instance_var slot since they are not allowed. We also implement a add_method_to_class method in our AbstractClass method table which will allow us to easily enforce the fact that all our methods use the same \&abstractMethodHandler (which will throw an appropriate error if called). We also throw an error with the create_instance method since AbstractClasses can be instantiated.

Next we define the Class meta-class (which is actually a subclass of the AbstractClass meta-class):

 {
    'class' => MetaClass,
    MetaClass => {
        'name' => 'Class',
        'parent' => AbstractClass,
        'instance_vars' => {
            Class => { 
                'instance_var' => {}
            }
        },
        'method_table' => {
            'create_instance' => { 
                return merge(
                    Class.parent.<instance_vars>,
                    { 'class' => Class, Class.<instance_vars> }
                ); 
            },
            'add_method_to_class' => ($class: $label, $impl) -> {
                $class<method_table><$label> = $impl;
            },
            ...
        }
    }
 }

Notice the parent slot, which points back to the AbstractClass meta-class. We also now include an instance_var slot into the Class meta-class's instance_var slot, all the other slots are defined in AbstractClass. This allows our Class meta-class to have instance variables, which we denied to our AbstractClass meta-class.

Next is the create_instance meta-class method which merges the instance_vars of the parent (AbstractClass) with the instance_vars of the Class, and of course our add_method_to_class meta-class method now accepts a method implementation ($impl).

Okay, so now we have a basic object model here. We have abstract classes as well as regular basic everyday classes. Simple, right? :)

Now comes our last meta-class, the ThreadSafeClass.

For the purposes of my example, I am keeping it very simple and saying that in order to be thread safe, every method must acquire an instance specific semaphore prior to executing, and then release that semaphore after execution.

Here is what that would look like:

 {
    'class' => MetaClass,
    MetaClass => {
        'name' => 'ThreadSafeClass',
        'parent' => Class,
        'instance_vars' => {
            'semaphore' => <semaphore>
        },
        'method_table' => {
            'add_method_to_class' => ($class: $label, $impl) -> {
                $class<method_table><$label> = ($self: @args) {
                    $self<semaphore>.acquire();
                    @return_vals =
                    $self.invoke_method($impl, @args);
                    $self<semaphore>.release();                        
                    return @return_vals;
                }
            },
            ...
        }
    }
 }

This is somewhat of a simplistic implementation, but it serves to illustrate the power of meta-classes quite well. We need to add the semaphore instance_var slot since all our instances will need one. And then in the add_method_to_class, we simply wrap the method in our own thread-safe wrapper.

So as you can see, using meta-classes we can very easily create fairly robust object models quite simply.

[... more to come later ...]

AUTHOR

Stevan Little <stevan@iinteractive.com>