Not too long ago, someone asked me to explain the fundamental differences between static languages, such as C# and Java, and dynamic languages, such as Ruby and Python.
Although I knew that duck typing and not resolving method references until runtime allowed for some nifty metaprogramming tricks that translated into not having to write as many lines of code, I was not able to provide any concrete examples of how this was done. This left both me and my friend feeling unsatisfied with my explanation.
Although learning about something at a high level through books, blogs, and podcasts can be useful, I often find that it tricks me into thinking I know more about something than I actually do. I’ve just had way too many experiences where I thought I understood something only to be proven wrong when I finally had the opportunity to roll up my sleeves and tackle some concrete examples and problems.
This is why I have recently been on the look out for some more concrete examples of how dynamic languages allow for some nifty feature that just aren’t possible in static languages (or at least too difficult to be feasible).
I finally found a good one this last week while reading about Dynamic Finders in the book Agile Web Development With Rails. These are convenience methods provided by Rails that allow you to query on various columns by simply following a naming convention for methods that concatenates Find_By or Find_All_By with the column names you want to filter on.
So, instead of having to create and implement a method like this (example from Castle’s Active Record):
1: public static Card[] Find_By_CardType_And_ExpirationDate(int cardTypeId, DateTime expirationDate)
2: {
3: return FindAll(Expression.And(Expression.Eq("CardTypeId", cardTypeId),
4: Expression.Eq("ExpirationDate", expirationDate)));
5: }
6:
7: ...
8:
9: Card[] cards = Card.Find_By_CardType_And_ExpirationDate(cardTypeId, expirationDate);
All you have to do is make the call to the non-existent method that contains your column names and Rails will dynamically generate the method for you.
1: @cards = Card.find_all_by_cardType_and_expirationDate(cardTypeId, expirationDate)
How does this work?
Unlike with static languages, Ruby doesn’t require that this method actually exists at compile time. Instead it just throws a MethodMissing exception at runtime if the method you called doesn’t exist and then allows you to react to this condition by simply defining\overriding the required method_missing signature in your class.
In order to provide Dynamic Finder functionality, Rails uses something like the Regex logic below to parse the method name that was passed in to the method_missing override and then use the resulting tokens to dynamically generate the method and its implementation, “spot weld” it onto your class, and then execute it.
1: def method_missing(method_id, *arguments)
2: if match = /find_(all_by|by)_([_a-zA-Z]\w*)/.match(method_id.to_s)
3: # find...
4: elsif match = /find_or_create_by_([_a-zA-Z]\w*)/.match(method_id.to_s)
5: # find_or_create...
6: else
7: super
8: end
9: end
Neat, huh?
I found this example particularly compelling because I am working with both frameworks right now and so far find Castle’s implementation to be remarkably similar to Rail’s Active Record even though it was created using a static language (C#).
The fact that Dynamic Finders aren’t available in Castle’s Active Record and that they are implemented by a decidedly dynamic feature leads me to believe that this was one of the areas that dynamic languages outshines static languages. However, I am a self-avowed newbie in this area, so I welcome anyone with a deeper knowledge of either framework to correct me if I’m wrong.
I would also love to hear about any other concrete examples of metaprogramming magic that is made possible (or much easier) by a dynamic language feature.
If you’re interested in learning more about dynamic languages, then I highly recommend Steve Yegge’s recent blog post, Dynamic Language Strike’s Back. It’s a rather lengthy transcription of a recent talk he did at Stanford on the topic, but it addresses some questions I had about dynamic languages in terms of tooling, performance, and maintainability in some very novel and comprehensive ways so it was well worth the reading investment.
Popularity: 28% [?]