-
JavaScript and alike are very proud about their first class citizen methods/functions - but what are they? Does Ruby have them? If so, in what form? These are a few of the questions I will try to answer in this post.
If you ever traverse through some Rails projects you will most likely come across the use of Module#alias_method_chain. It basically places a function as a proxy in between the original call and the actual method call. It makes it easy to define some Aspect oriented programming which makes it easy to add logging and performance measuring to a method call.
Last autumn I played around with some Ruby metaprogramming regarding methods. I decided to write a simple DSL for doing some AOP in Ruby. Not because it is going to be overly useful for everyone but more for the fun of it.
Enter Tivoli.
What better than a DSL to explain the functionality.
There are a few gems already providing this functionality and much more for Ruby http://aquarium.rubyforge.org/ and a few more https://www.google.com/search?btnG=1&pws=0&q=gem+aspect+oriented.
My core thoughts on Tivoli are: It should be dead simple, not pollute any object and provide a way of doing AOP in an adhoc manner. (Meaning existing code should not be concerned that we are logging or measuring performance).
About this time a year ago I started my deep dive into JavaScript, a language that I had no previous intimate knowledge about. I knew only the parts i had previously needed to get around jQuery and all was good. Throughout the past year of studying the language I came around to find the elegance and horrible extremities of it.
Let me guide you through the thoughts that led me to Tivoli.
Disclaimer: We are about to tread into meta programming, please choose cautiously what you would use in your production code.
Methods in Ruby are quite different from the functions we see in JavaScript.
To recap: In JavaScript functions are like any other object. They can be passed around, assigned attributes and so on. This is one of the core features of JavaScript and we should be quite happy that such an old (17 years) dynamic language lets us create highly dynamic code with functions as first class citizens.
In Ruby we clearly distinct methods from blocks, which are the two tools we as developers have to mix in a dynamic control flow. Methods are always defined on a class, even though this might be the eigenclass or the global main object. Methods can only be called in context with an object and a class. In contrast to JavaScript, where it is possible to pass null through a #call or #apply call. Blocks, namely procs and lambdas, on the other hand can be passed around in the same way as JavaScript functions and are often passed in a method through the normal block syntax, but also as normal parameters with the flip side of missing out on nicer syntax.
In JavaScript the only way to imitate Ruby blocks would be by passing in the function as an argument which reminds us of the above syntax. Maybe changing the minimal way of defining a function could make the below more appealing but for now i find myself not using anonymous functions/procs/lambdas and instead naming them for cleaner syntax.
A word on syntax:
In JavaScript we have to use parentheses when calling a function which feels nice and cosy like any “c-like” language. This has the benefit of letting us treat references to the function without any arguments or parenthesis as a reference and not as a call which in turn brings us the lazy feature of passing functions around in JavaScript and evaluating them when we choose in the context we see fit.
In Ruby, things are quite different for methods but similar for procs/lambdas. Ruby methods are in fact called when referred without parentheses and arguments which makes for nice short and clean syntax and compensates by using blocks when functionality needs to be passed around.I think the design choices in Ruby are quite solid and bring the nice flexibility that we come to expect when expressing ourselves in Ruby. I encourage reuse of blocks in this manner as it is often a feature people tend to overlook as a way of DRYing up their code.
Even the block syntax feels clumsy at times for simple cases. Which got us the Symbol#to_proc and encourages some to work outside of the simple cases.
The short syntax that you see above unfortunately only captures the cases when the methods are in fact defined on the object.
So given a class method in this example JSON#parse we could create a proc that proxies the call and reuse it.But would it not be cleaner to actually pass JSON#parse much like we would have done in javascript? Turns out we can - with #method.
Reading the docs on the Method Class confirms that a Ruby method is defined in the context of a object, meaning that there is a #reciever and an #owner method on a Method.
This got me thinking… There’s got to be a way of reusing a method’s definition for multiple class and at the same time satisfying my needy DRY addiction while removing the overhead of having a separate block for proxying every call.
We define a method #twice on Numeric
Now lets say we want to reuse this method. How do we get around so that a method is bound to a reciever?
We want a piece of functionality that we could pass around like any other object just as in JavaScript. One could always call 1.method(:twice) but that would return an object that is bound to 1.Enter at Method#unbind
“Dissociates method from its current receiver. The resulting UnboundMethod can subsequently be bound to a new object of the same class (see UnboundMethod).”.
UnboundMethod says“Unbound methods can only be called after they are bound to an object. That object must be be a kind_of? the method’s original class.”
Ok, but what if I don’t have an instance of the class that I am copying the method from? Module#instance_method provides what we need. Please note that it does not in fact return a Method as one would expect at a first glance but an UnboundMethod since it would actually not be bound to an object.
Let’s try to bind the Numeric#twice that we defined above to StringThis turns out to be a limitation in Ruby, at least MRI. Since Ruby is slightly evil, not letting us jailbreak twice, let’s bring in the heavy artillery ourselves: evil-ruby. Unfortunately this does not work with the latest MRI (1.9.3) atm, but highlights what lengths one would need to go, to achieve a solution to our problem. This makes me think that this is probably not a good idea out of a design or performance perspective.
Ok, but what CAN we do?
If we start off defining our functionality in a proc, it turns out to be quite simple to share it among classes.But when you come to think about it, you could have an abstraction layer on top of this piece of functionality which included defining multiple methods that should be included and Voila! You have reinvented Module.
I have hopefully teased you enough to have a look at the implementation:
https://github.com/Fonsan/tivoli/blob/master/lib/tivoli.rb
This is a first blogpost of many to come from me, I hope you enjoyed the tour, please join in on the discussion below