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 :-)

Friday, 04 May 2007

1: Ruby blocks and iterators

The first time I saw Ruby code, the 'blocks' stood out like a sore thumb. They were everywhere. Sometimes it was obvious what the code did, sometimes not. I was intrigued. Of course, once I started programming Ruby, I learned to love blocks. They are a really cool and expressive way of doing things that can be quite a lot less convenient in Java. I am not going to explain all the details of what blocks are and how they work (see ‘Programming Ruby’ if you want to know that). I’m simply going to show you a few examples, to give you an idea.

Iteration

Consider the classic ‘for’-loop. In many C-like languages the syntax is the same as in Java:
Java for loop
Java for loop results
In Ruby there are a few ways of doing what the above code does, but using blocks you can be very concise:
Ruby Range.each
Ruby Range.each results
One important point about this is that the each method is actually called only once, not three times, but it in turn will call back into the block three times, each time passing the latest value of ‘i’.

Code injection

In java we can also pass code into a method to be called when necessary, but it requires defining specific interfaces. Here is an example based on some real code we have in a regression testing framework used to test one of our products every night:
Java code injection
Wow, that was verbose. We needed to define the interface ‘CustomTesting’, and each time the main test method ‘doTest’ is called we pass an implemetation of that interface so that it can be called in the middle of the test. This seems simply like a much more verbose way of doing what Ruby blocks do, and in this example that is true. But Ruby takes this one critical step further. The local scope available at the time the block is passed into the method is still available within the method. That is not true in Java, and is a very important difference.

Ruby method blocks

So, what does this look like in Ruby.
Ruby method blocks
Ruby method blocks results
Much simpler than in java, that’s for sure. But this example does not show anything beyond the java example. Two things ruby supports that really interest me are:

  • Local scope
  • Metaprogramming

There will be examples of local scope in laters snippets, and I have decided to devote the entire next snippet, all 15 minutes of it, to metaparogramming.

Ruby blocks and metaprogramming

Although I have decided to devote the next snippet to metaprogramming, here is a quick taster:

Ruby method blocks metaprogramming
Ruby method blocks metaprogramming results
This is very similar to the previous example, but I have actually modified the Range class to have a new method to call, and I do so instead of the ‘each’ method I used before. You can even change the ‘each’ method to do something different than before. Next week I’ll discuss this in more detail.

Mini tech-talks

Recently I've begun giving a weekly series of ultra-short tech talks to the other developers in our office. The original problem we had was that, while we were all interested in learning about, or hearing about, technologies outside our working domain, it was generally not possible to spent office working hours on projects not directly of benefit to the company. The solution has been to give 15 minute talks during normal coffee-breaks. One really positive aspect of this is that it forced me to focus on a single specific 'hot' topic, and thereby blocking my usual tendency to lecture on for hours. While the resulting tech-talks are not comprehensive, they were easily digestible. Anyway you can always go to the books referenced below if you want something comprehensive.

These ‘snippets’ follow my progression as I learned Ruby, JRuby and Rails from the books I read, internet articles, and trial-and-error with real scripting projects I did. Each week I thought of something that was of particular interest to me as a Java and sometimes Perl programmer, and presented it in a 2-4 slide ‘snippet’. Where possible I used examples directly relevant to the work we do, which is mostly Java programming of data models and numerical analysis in mobile phone networks (not mobile phones). Sometimes I used examples from the books I read along the way:


For a more specific Ruby-2-Java comparison, see the extract in ComputerWorld of the book ‘Rails for Java Developers.’ This is a nice soft intro to Ruby for Java developers. However, while my snippets are not as complete, I think they are more interesting and relevant to me, of course. And I hope they will be of interest to others too.