Metaprogramming Pattern No. 1: Self-specialize

Posted by Nick Sieger Tue, 11 Jul 2006 03:48:00 GMT

One of the biggest aspects of Ruby that I’ve been digging are the metaprogramming facilities, many of which draw from the “code as data” philosophy that comes from Lisp. Metaprogramming has become somewhat of a buzzword in the Ruby community, about as popular as “domain specific language” in terms of its presence in the titles of conference presentations and the like.

So it seems to me that, while a good many smart people are talking and writing about metaprogramming, that we haven’t yet started cataloguing all the different techniques in a shareable way. Is it time to start writing a catalog of metaprogramming patterns? Or do I risk being taken to trial for attempting to unveil the rubyist’s magic tricks?

No, what I am really looking for is the metaprogramming ubiquitous language. Ruby has a lot of language-level features that facilitate metaprogramming, but without a strong jargon to describe what’s going on. Sorry, but module_eval, instance_eval, metaclass vs. singleton class vs. eigenclass, method_missing etc. just don’t cut it. So here’s a first shot at re-invigorating the conversation and taking it to the “HNL” (‘hole nuther level). So herewith begins Metaprogramming Pattern No. 1: Self-specialize.

Why is this one number 1? No reason, pretty arbitrary. I haven’t taken the time to document any more yet. This will be an ongoing project for me.

Self-specialize

You have an algorithm or a “method object” that you wish to make flexible by parameterizing with additional code and/or data that isn’t known at the time you wrote the original class definition.

Therefore, write a method that redefines itself in the singleton class of the currently instantiated object, then re-sends the message to the customized method. In a way, this is simply memoizing the method body itself as a speed-up.

Trivial example:

class Foo
  def initialize(p)
    @prefix = p
  end

  def result(val)
    specialize_result
    result val # send the message again
  end

  private
  def specialize_result
    method_decl = "def result(val); "#{@prefix}: #{val}"; end"
    instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
  end
end

f = Foo.new("foo")
f.result("hi") # => "foo: hi"
g = Foo.new("bar")
g.result("hi") # => "bar: hi"

Why would you do this rather than writing the logic into the original method? It’s hard to justify use of the technique in the example above. But it could be used to DRY up tedious, redundant code that for performance reasons you would prefer to have inlined rather than invoking an additional instance method.

OK, so honestly I can’t come up with a decent reason to do this yet; I haven’t done enough metaprogramming to have had the need for it. Still, it seems like a nifty enough trick and maybe it will come in handy for you. This does show you just how dynamic Ruby’s method resolution is, that you can suddenly define and call a different method implementation inside of the method itself!

This technique was spotted in the rewritten routes implementation for Rails 1.2 (currently on the trunk) -- see the #write_generate and #write_recognize methods.

Update: Good timing. _why just posted much more succinct description of the same phenomenon, with better examples.

Posted in ,  | Tags ,  | no comments | no trackbacks

Comments

Trackbacks

Use the following link to trackback from your own site:
http://blog.nicksieger.com/articles/trackback/33