Ruby Weekly is a weekly newsletter covering the latest Ruby and Rails news.

Ruby Refinements: An Overview of a New Proposed Ruby Feature

By Peter Cooper / December 6, 2010

finches.pngSignificant 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.

Comments

  1. raggi says:

    Have fun debugging that.

    Hint: you're going to have to walk up frames to see what methods are what.

  2. postmodern says:

    I wish `include` and modules always had this behaviour. Also the performance implications scare me a bit, better to wait and see.

  3. joesb says:

    @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.

  4. seydar says:

    @raggi, could you elaborate more on your concern? It sounds like it will be just as hard as normal debugging with modules.

  5. skrat says:

    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?

  6. Dave says:

    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).

  7. Christian Romney says:

    > 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.

  8. Guoliang Cao says:

    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.

Other Posts to Enjoy

Twitter Mentions