top | item 1947467

Refinements in Ruby — Monkey patching for friendly monkeys

64 points| judofyr | 15 years ago |timeless.judofyr.net | reply

11 comments

order
[+] fizx|15 years ago|reply
This feels a lot like scala implicit conversions/views made into idiomatic Ruby.

In scala, you could write an "implicit function" that converts Foos to Bars. If and only if you imported the function, you could then (1) pass an instance of Foo to a function accepting Bars and (2) call methods of Bar on a Foo.

For example, if you defined an implicit mapping from Int to IntWithTimeMethods, you could then call something like 1.seconds_from_now, and get a Time back. You can see something like this at work in https://github.com/robey/xrayspecs/blob/master/src/main/scal...

In scala, it's not required that the class you convert to is a subclass of the original, but it's a really common use case. This "refine" behavior gives you this same ability, but just with an anonymous subclass.

FWIW, I like Scala's implicit conversions. Usually, I see them with things like 1.seconds, or object.toJson, and then you just look among the imports for something referencing time or json. I guess people could think up absurd uses, but then, those people were writing bad code anyways.

[+] techarch|15 years ago|reply
Thanks for the summarized analysis Magnus. The syntax for defining and using refinements looks very clean. I'm looking forward for this feature to be included natively.
[+] rue|15 years ago|reply
I think Classbox is the better way to go, but this is an improvement. The performance hit is currently very significant, but I suspect it can be optimised to a negligible level.

As with the .append_features hack, I think it is only good that refinement in no way implies inclusion. It will probably be better to keep the two types of modules explicitly separate.

[+] xentronium|15 years ago|reply
I have mixed feelings about this.

It's good because you gain an ability to scope your monkey patches.

It's bad because it incentivizes monkey-patches.

[+] generalk|15 years ago|reply
I don't get it. What's bad about incentivizing monkey-patches that don't interfere with other code?
[+] jerf|15 years ago|reply
The badness of monkeypatching is its ability to make non-local changes that stomp on other code. Monkeypatches aren't bad in a general sense. Take away the stomping behavior and you've taken away the badness.

(They might still be "dangerous" but you've moved them out of the "suicidal" territory I believe non-scoped monkeypatches live in.)

Incidentally, Perl has been able to do this for a long time. I've gotten a lot of mileage out of:

    local *{'Some::Module::method'} = sub { ... };
    my $thing = new Some::Module();
    ...
    $thing->method(...);
    ...
It's great for testing, great for modifying the way libraries work locally without globally modifying them (in my case, hacking a bit on code shared by many projects in such a way that I can't really add this thing I really need), and so on. A great addition to Ruby that goes a long way towards mitigating some of my fundamental complaints about the language.
[+] ericb|15 years ago|reply
In many other languages it is agonizing to see a solution that does 95% of what you want. Without that last 5% it is unusable for your purposes and you are forced to reinvent a wheel.

As dangerous as they are, there is a reason people monkey patch. Refinements will just help remove the downsides.