Friday, 11 May 2007

2: Ruby metaprogramming

Last week I ended with a short example of blocks together with metaprogramming. Today we’ll start with that example:
Ruby blocks and metaprogramming
Ruby blocks and metaprogramming results
Although we successfully modified the Range class by adding a new method to use instead of each, we could have modified each itself, and I will show that later. But first let’s define metaprogramming.

Metaprogramming

Here is an extract from the wikipedia definition of metaprogramming:
Metaprogramming is the writing of programs that write or manipulate other programs (or themselves) as their data or that do part of the work that is otherwise done at run time during compile time. In many cases, this allows programmers to get more done in the same amount of time as they would take to write all the code manually.
The language in which the metaprogram is written is called the metalanguage. The language of the programs that are manipulated is called the object-language. The ability of a programming language to be its own metalanguage is called reflexivity.
A simple example of a metaprogram is this bash script, which is an example of generative programming:
Bash metaprogramming


Classes and Objects

Back to our example, where we added the method myTest to the class Range. We could instead have added the method to only a single instance of Range:
Object methods
Object methods results
Take a look at ‘Programming Ruby’ for more examples.

Mixins

Ruby does not support C++ style multiple inheritance for the same reasons Java does not. It is seen as too easy for the programmer to hang themselves. However Ruby supports a concept that lies somewhere between java’s interfaces and C++’s multiple inheritance. Here is an example from Bruce Tates ‘Beyond Java’:
Mixin
Mixin results
We have created a module called ‘Greetable’ that provides an implemented method ‘greet’. If Greetable is ‘mixed-in’ to a class that has a defined attribute @name, then that class will have a working greet method. If the class does not have @name, it is created dynamically by greet, refers to nil, which in turn displays as an empty string in the greet method. As a Java programmer you can almost hear the ‘NullPointerException’ that would happen in Java could get anywhere close to this capability.
Let’s try a mixin that modifies a pre-exiting method. Ruby defines ‘inspect’ on every Object, and it returns a description of the Object. Let’s redefine it:
Mixin inspect
Mixin inspect results
Wow. In the same bit of running code we called the normal inspect, redefined it and called it again. Dynamic languages rule!

Interceptors

Another example by Bruce Tate, available both online and in ‘Beyond Java’, demonstrates the possibly dangerous power you have when redefining methods, in this case to emulate the java concept of ‘method interception’:
Interceptors
Strangely enough this example did not work in the normal ruby interpreter, only in irb, the interactive interpreter, and I never did discover why.

Full circle

I promised I’d show how the original example can be changed to rather redefine the Range.each method. Well, here it is:
each
each results
We use the Ruby method ‘alias_method’ to rename the each method to original_each, and then we redefine each to call original_each, but with some small changes. Obviously this example does not do much, but the possibilities are limitless.

This 'snippet' was a little longer than planned, and took nearly 30 minutes to present, including questions. So next week I'll present only a single example of Threads in Ruby, and keep it short :-)

No comments: