Ruby Refinements: An Overview of a New Proposed Ruby Feature
Significant and serious improvements to the core Ruby language come along as infrequently as TextMate updates. Given that TextMate has had an update recently, an important new Ruby feature was sure to be just around the corner and it is: refinements! Shugo Maeda (who works with Matz and developer of mod_ruby) presented the idea at RubyConf 2010 last month.
In a nutshell, refinements clear up the messiness of Ruby's monkey patching abilities by letting you override methods within a specific context only. Magnus Holm has done a great write up of the concepts involved, and presents a good example:
module TimeExtensions refine Fixnum do def minutes; self * 60; end end end class MyApp using TimeExtensions def initialize p 2.minutes end end MyApp.new # => 120 p 2.minutes # => NoMethodError
In this example, rather than adding the minutes
method to the Fixnum
class in the typical way, it's been added as a "refinement" within a module which is then used elsewhere to spread the context of the change. Going a step further, minutes
could be defined on Fixnum
in many different ways within different classes and none of them would tread on each other's toes (or, at least, this could be mitigated).
Good for Rails?
Shortly after Magnus' explanation, Yehuda Katz stepped in with his own take in Ruby 2.0 Refinements in Practice - mostly focused around the impact refinements could have on Rails. Refinements seem particularly useful in the context of Rails considering how much monkeypatching ActiveSupport does in implementing methods like minutes
, days
, and so forth.
Yehuda also looks at a situation where monkey patches in one library tread on the patches made in another library and how refinements could help clear things up.
Not so Good for Implementations?
Charles Oliver Nutter (of the JRuby team) also wrote up his take, and it's somewhat less positive. He argues that implementing refinements will reduce performance across the board and introduce several structural complexities, especially around the combination of runtime-mutable lexical scoping structures, concurrency, and method caching concerns.
December 6, 2010 at 3:02 am
Have fun debugging that.
Hint: you're going to have to walk up frames to see what methods are what.
December 6, 2010 at 6:33 am
I wish `include` and modules always had this behaviour. Also the performance implications scare me a bit, better to wait and see.
December 6, 2010 at 9:48 am
@reggi:
I think you got it backward, what you described was what already happens with Ruby's monkey patching.
The point of refinement is so that you don't have to walk up the frames to know what refinement are applied; it's supposed to be psuedo-lexically scoped.
December 6, 2010 at 1:13 pm
@raggi, could you elaborate more on your concern? It sounds like it will be just as hard as normal debugging with modules.
December 6, 2010 at 4:05 pm
Patching the patches with a language feature. Monkey patching IS NOT a feature of Ruby, it's programmers' bad practice, mostly popular thanks to monstrosity of ActiveSupport and such. Have to agree with raggi, debugging hell. But still an improvement over current status. I mean, what price are you willing to pay for 1.day?
December 6, 2010 at 11:02 pm
Why have ruby-core preferred refinements over classboxes?
Refinements do not have local rebinding which leads, in my view, to some really counter-intuitive results (see http://goo.gl/4P4Wa slide 39 and here http://goo.gl/p7ReQ).
December 7, 2010 at 12:00 am
> I mean, what price are you willing to pay for 1.day
A pretty high one, especially given the fact that I've never had a problem with that particular method. Now, I concede that I wouldn't wish for a world where every library extended base types on a whim. But having a few basic libraries specifically devoted to providing extended functionality (think Facets) seems like a good idea to me. Then application developers can leverage them if they so desire. The problem only comes when library designers introduce dependencies and haphazardly or without adequate documentation.
December 8, 2010 at 3:47 pm
I like this in general. It is a much better alternative to monkey-patching. If the performance impact is really a issue, then maybe we should have a second thought.
Introduce two keywords for one single feature is a little too much though. "using" is not necessary here and "include" will suffice because refinements are new and include can be extended to handle them.