16. August, 2004
16. August, 2004
in by Michael Neumann
I read about Python Decorators on Lambda-the-Ultimate. They will be part of Python 2.4. But, was it really worth to add special syntax for something that can be done without problems in a different way? Isn’t Python breaking it’s own principle "There’s only one way to do it"? Will Python become a second Perl? Which so-called "feature" will be added next? Don’t get me wrong, Python is a valuable language!

The Ruby language instead is pretty constant since it’s very beginnings (version 1.0 of December 1996). There are no visible changes in the language (but in the library!) except the introduction of class-variables in 1.4 (or was it 1.6?).

Decorators in Python

Instead of:

def f(...)
  ...
f = synchronize(f)

you can now write:

@synchronize
def f(...)
  ...

The part after the at-sign is simply a function (synchronize is our case).

Decorators in Ruby

def f
  ...
end
synchronize :f

That’s pretty the same as the first Python example shown above. But can we simulate the second Python example in Ruby? Yes we can! We could do this in pure Ruby, but that would be a bit more advanced. Therefore I’ll show you how to do it with a minor change to the Ruby interpreter. Minor means that the change does not break any existing code and that only one line needs to be changed:

--- eval.c    2 Aug 2004 08:52:53 -0000  1.686
+++ eval.c    16 Aug 2004 12:52:28 -0000
@@ -3701,7 +3701,7 @@
                rb_add_method(rb_singleton_class(ruby_class),
                              node->nd_mid, defn, NOEX_PUBLIC);
            }
-         result = Qnil;
+         result = ID2SYM(node->nd_mid);
        }
        break;

With this patch applied, a method definition will return the method name as symbol. Let’s try this:

x =
def f
end
p x # => :f

Now we can "simulate" the second Python example in Ruby:

synchronize def f
  ...
end

# or with the decorator on a separate line

synchronize \
def f
  ...
end

Note that the backslash after the synchronize in the second part of the example is neccessary! This tells Ruby that the argument is on the next line.

And we can even cascade "decorators":

private memoize synchronize \
def f
  ...
end

Of course this only works if each involved "decorator" method passes the method-id through.