Bindings, Platforms, and Innovation
This presentation focuses on the Internet and separating myth from fact, history from the future, and the mundane from the imaginative. Bob Frankston presents a vision of what could and should be.
Tracking change and innovation in the enterprise software development community

Posted by Werner Schuster on Jul 11, 2008 02:00 PM
Rails developers who watched the recent Ruby 1.8.7 preview releases, soon noticed something about the 1.8.7 Preview 1: it broke Rails. The reason was the addition of a method Symbol#to_proc, which was backported from Ruby 1.9. Adding this method allows to write certain code in a more compact way (see details about Symbol#to_proc).
Give-away eBook – Confessions of an IT Manager
Agile Development: A Manager's Roadmap for Success
Usage Landscape: Enterprise Open Source Data Integration
The Agile Business Analyst: Skills and Techniques needed for Agile
So what happened? Rails had already added the to_proc method to Symbol. However... the method Ruby 1.8.7 Preview 1 added had a slightly different behavior than the one Rails added.
Fortunately, Rails has quite a few users, so the problem was quickly reported, and the final version of Ruby 1.8.7 has a version of Symbol#to_proc that works.
class String
def foo
"foo"
end
end
puts "".foo # prints "foo"
Object - in that case, every subclass of it has the added method(s). This is a bigger problem than clashing with another monkeypatching library. Why? Because every class in the system is derived from Object, thus the added method is now in the name space of every class. So... unless the added method has a name that includes, say, a SHA-1 hash value, there's a chance it'll clash with another method. Symbol#to_proc method - libraries added this to allow for a special, terse syntax for certain operations. In Ruby 1.9, this was added to the standard Ruby stdlib's Symbol class. This shows another source for name clashes: if a name is general enough, a future Ruby version might include the method too. While it might be fine if the method does the exact same thing - it's a problem if it doesn't. In that case, redefining the method can break the system, i.e. the Ruby stdlib and all it's clients that rely on the behavior of the standard Ruby library. OutlinePage p = editor.getAdapter(OutlinePage.class);The class of the
editor object can either directly return an OutlinePage object that knows how to display an outline for the editor's content. If this particular editor doesn't implement Outline functionality, the protocol of the getAdapter method suggests to forward the call to a central lookup system. Which brings us to the extensibility/modularity part: even if the creator of the vendor hasn't supplied an Outline GUI, another Eclipse plugin can provide one. Advantage of the Adapter pattern: no need to modify domain classes to add functionality - the adapter logic contains all the logic to adapt from the desired interface to the original object. Allows orthogonal changes to the interface. No global changess necessary. For more information about Eclipse version of the Adapter pattern, read "What is IAdaptable?" by Alex Blewitt.a = "Hello"The effects of these changes are kept local to the object - no other classes or objects are affected. For more information and examples on when to use Singleton classes, see InfoQ's article "Using singleton classes for object metadata".
def a.foo
"foo"
end
a.foo # returns "foo"
aliasextensions.rb. By sticking to this convention, all extensions are instantly visible to anyone reading the code without requiring any special IDEs or class browsers that show where methods come from. It also acts as documentation of what classes are affected.
Classical modules systems support well the modular development of applications but lack the ability to add or replace a method in a class that is not defined in that module. But languages that support method addition and replacement do not provide a modular view of applications, and their changes have a global impact. The result is a gap between module systems for object-oriented languages on one hand, and the very desirable feature of method addition and replacement on the other hand.
To solve these problems we present classboxes, a module system for object-oriented languages that allows method addition and replacement. Moreover, the changes made by a classbox are only visible to that classbox (or classboxes that import it), a feature we call local rebinding.
public static int WordCount(this String str)As you can see, the method gets the
this pointer to the object it's working on. To make the extension visible:
using ExtensionMethods;And done - you can now use the new method:
string s = "Hello Extension Methods";The benefit of this approach: the extension method is only visible in code that explicitly imports it.
int i = s.WordCount();
inline method calls to find the C code that does the work).
I take away the opposite lesson from the Symbol#to_proc incompatibility problem. What I take away is "use open classes (within reason) because it will give you great functionality that you need without waiting for some official vendor to support it". Don't get my wrong. Alternatives should certainly be considered. Open classes are not the solution for every problem and your article lists and explains some great alternatives. But don't be scared of open classes either. Rails popularized the Symbol#to_proc method and it may not have been included in Ruby 1.9 and backported to Ruby 1.8 had Rails not popularized it. Symbol#to_proc is an extremely useful method that has saved countless developers countless snippets of time. By having open classes Rails was able to include this functionality YEARS before some official vendor supported it. Even once the vendor did support it there was a slight incompatibility which was resolved. Now each case is different. This time the issue was resolved by the official vendor changing their code. Maybe next time the application (or framework) will need to change it's code to get the issue resolved. But either way we traded countless amounts of time saved for just a few moments of "hmmm.... I guess we need to change this to that". Sounds like a big win for me. Stop being afraid of the dynamic language and embrace it. Have good testing to catch issues like this before deployment and then love the benefits you reap. Don't go back to the old non-agile way of doing things where you write reams of extra convoluted code just to ensure you are not affected by some unlikely issue that might happen in the future. You are going to have compatibility problems no matter how careful you are. So just use common sense and deal with anything when it comes.
> redefining the method can break the system, i.e. the Ruby stdlib and all it's clients that rely on the behavior of the standard Ruby library. Only for "clients" in that process or that require the offending library. The way it's written - and with system in italics - it sounds like you can hose the standard library for all Ruby applications running on a box by using open classes.
"putting them all in a file extensions.rb" is a good suggestion, but I would distinguish between opening a class to fix a bug or add a feature *which you intend to eventually make it's way upstream* (aka monkey patching) versus opening a class as a permanent design choice. In the first case, the filename convention in Zope is "patch.py" (and __init__.py being the worst filename location for a monkey patch since those source files are less commonly used and so harder to detect).
There is also a "monkey" package for Python which only applies the patch if a hash signature of the patched method is unchanged, which is an extra careful way of patching so that the developer can be alerted when the upstream source being patched has changed (http://pypi.python.org/pypi/monkey).
Monkey patching was originally called "guerilla patching" (a term started at Zope Corp.) because a developer who disagreed with a choice made upstream could apply their own behaviour without needing to have the patch reviewed and applied upstream. Guerilla patching was misheard as Gorilla patching and from that the term Monkey patching was invented to sound "less forceful than a gorilla". Zope developers still tend to use this term to only apply to opening classes to fix or extend them to address undesirable behaviour upstream - other use-casses for re-opening a class is usually just called "dynamic class modification". However, in the larger Python and Ruby communities this distinction isn't usually made and Monkey patching usually refers to any re-opening of class, regardless of intent.
The adapter pattern developed in Zope (and the convention-over-configuration technique applied by Grok which avoids the need for XML sit-ups) rocks, and is better alternative in many cases than the venerable monkey patch. Syntactically Python adaptation is much more concise than the Java equivalent :)
Eclipse/Java:
OutlinePage p = editor.getAdapter(OutlinePage.class);
Zope/Python:
p = IOutlinePage(editor)
BTW, it's Zope and not ZOPE, which is akin to saying RUBY or JAVA, all-caps are yucky :P
Hmm... the word "clients" was meant to mean "clients of libraries", ie. if a piece of your application uses - say - library X, it's a client of that library. I can see how it can be misunderstood as "all apps on the system"... but I thought it was clear that's not really possiblen anyway.
I agree - having this power definitely gives Ruby an edge and allows tons of experimentation which is impossible or very hard in other systems ("very hard" as in, eg., having to adapt the language's compiler or runtime to do so, etc). BTW: experimentation like this is fine enough - that doesn't mean that you want to put something like this in a library. We recently had a presentation on how Mingle was built: http://www.infoq.com/presentations/ford-rails-based-mingle One thing that I noticed was the liberal use of Open Classes - something which is fine if you build an application which is _never_ going to be part of something else. However: if you release a library, you have no idea at all where it'll end up and with which other libraries it'll share an address space. How would you like it if you just spent a day or two tracking down a problem... only to find out that developers of library A had to slap some odd method on Object, just to save a few keystrokes. There is, however, the issue that it's very easy to use open classes... and it's very easy to ignore the very real issues. Mind you: there are ways to avoid these issues _AND_ reap the benefits - I gave a few solutions in other languages (Classboxes, Extension Methods), and there are more which come as a neat side effect of strict enforcement of modularity: eg. http://gbracha.blogspot.com/2008/03/monkey-patching.html Or watch Gilad Bracha's talk on Newspeak: http://www.cincomsmalltalk.com/blog/blogView?showComments=true&printTitle=Gilad_Brachas_Keynote_at_Smalltalk_Solutions_2008&entry=3393562926 So, yeah: use Open Classes... just don't patch like a monkey...
This presentation focuses on the Internet and separating myth from fact, history from the future, and the mundane from the imaginative. Bob Frankston presents a vision of what could and should be.
This article explores the use of JBoss and jBPM to implement design solutions that effectively address the issue of orchestrating long running activities.
This presentation covers the use of graph databases as an optimal solution for data that is difficult to fit in static tables, rapidly evolving data or data that has a lot of optional attributes.
This session introduces Real Options and shows how it can help in running your project. Real Options is a decision-making process that can be used to manage risk.
This article discusses the use of bindings on services and references (including the instance of non-configured bindings) as the means to implement SCA communications in a Web and SOA environment.
After a short introduction to DSLs, Scott Davis plays with the keyboard showing how to approach the creation of a DSL by typing working snippets of Groovy code that get executed.
IBM Rational and InfoQ present, Scaling Agile with C/ALM, an eBook showing organizations how to become “finely tuned software delivery machines” by enabling team integration and scaling.
Amanda Laucher presents a real life enterprise application written in F#. She shows actual code snippets, explaining design decisions and suggesting how to use some of the F# constructs.
5 comments
Watch Thread Reply