Monkeypatching is Destroying Ruby

Along these lines, I read a chapter in Design Patterns in Ruby last night that suggests using Ruby's open classes as one possible way to implement the Adapter pattern. The book includes a pretty good discussion of the plusses and minus to this approach and makes recommendations about when it's probably worth the trade-off. It's a good read.

James Edward Gray II

···

On Feb 24, 2008, at 10:02 PM, James Gray wrote:

On Feb 23, 2008, at 4:23 PM, Eric Mahurin wrote:

I usually only do monkey patching in the
following cases which have no reuse: top-level script, testing, and quick hacking.

I think it's the ideal tool for adding compatibility methods.

I can say that I agree with all pieces of your article. This issue is
not related to rails community, it's ruby programmers issue (but of
course there are programmers that not make such a mistakes). The same
goes with using modules as mixins when there should be used delegation
or inheritance and many other things. I say that Ruby is very sharp
knife. It's very powerful but you must know how to operate with it to
not hurt yourself.

···

--
Radosław Bułat

http://radarek.jogger.pl - mój blog

Thanks. I think the reason I'm making a special case of monkey
patching is that, at least in some subsets of the community, I'm
seeing it morph into almost a standard extension technique. How do
you extend ActiveRecord::Base? Why, you re-open it, of course!
That's how everyone does it!

This could be more of a Rails thing at the moment, but as I commented
earlier, Rails culture will increasingly become Ruby culture, because
that's where the new Ruby programmers are coming from.

···

On Sun, Feb 24, 2008 at 11:17 AM, Bob Hutchison <hutch@recursive.ca> wrote:

Aside from that, there isn't a lot to argue with in your blog posting.
Your points individually are fine and quibbling over insignificant
details on a mailing list is a waste of time (though it might be ideal
in a pub :slight_smile: In other words, I don't disagree with anything you say
until you start drawing conclusions. I *like* monkey patching and I
approve of its proper use. Furthermore, I don't special case the abuse
of MP, it is just like any other abuse of a feature: bad.

--
Avdi

The thing is, it is *a* standard Ruby mechanism of extension, and it
may often be the simplest--for them, even when it doesn't seem to be
for you. And that's the tricky thing about simplicity; its mostly not
objective, its mostly about what matches the way a given person
thinks. The upside of Ruby's embrace of "there's more than one way to
do it" is that there is likely to be a way available that fits the way
you think fairly naturally, no matter where you come to Ruby from and
how long you've been using it (as long as you've mastered the basics).
The downside is that the most simple, direct, and natural way for
someone else to do something in Ruby may not be the most simple,
direct, and natural way for you.

That being said, the hyperbolic title aside, your blog post seems to
be largely stuff I can agree with, though I think you've misplaced the
problem very slightly. I don't think monkey patching itself is a
problem, I think the problem is that there isn't a good enough body of
experience of what to do and what not to do with monkey patching.
(Both in terms of when monkey patching is the right solution and when
there is a better alternative, and what to do and avoid doing when
you've made the decision to monkey patch.) Unlike basic composition
vs. inheritance questions, and other OOP considerations where the
facilities have been common in industrially-popular languages for
quite some time and, even if there aren't cut-and-dried standards to
apply there is a considerable body of advice and experience that most
programmer's have been exposed to, monkey patching is a lot more of a
"wild frontier" right now.

But criticism like yours is an important part of the dialogue that
needs to happen to work out where the problems are and to file the
rough edges off so that monkey patching can become a well-understood
tool that can be used effectively where it is appropriate and avoided
where it is not.

···

On Sun, Feb 24, 2008 at 8:18 AM, Avdi Grimm <avdi@avdi.org> wrote:

On Sun, Feb 24, 2008 at 10:36 AM, Kevin Williams <kevwil@gmail.com> wrote:
> I agree with everything Avdi has said, with the caveat that James is
> also right here. Use with caution, document the crap out of any
> monkeypatching you do, and get on with life.

The "use with caution" part is key. All hyperbole aside, I'm not
saying we should never monkey patch. I'm just concerned when I see it
used by smart programmers as if it were the standard Ruby mechanism of
extension.

I don't think all language features are created equal. Some are more
dangerous than others. I think global variables are universally agreed to
be dangerous (in all languages), but most languages still support them
because they still can be useful. I believe monkey patching falls into the
same category. I think the point of this blog was to discourage people from
using it because it is dangerous. There are simple alternatives.

Any language feature can be dangerous. "OMG YOU CAN OVERWRITE FILES
USING File.open()?? ITZ DESTROYING US!" As James said, you just have
to know when, why, and how to use it properly. The idiom is a very
powerful one if you use it with extreme caution. :slight_smile:

I don't see the big advantage of Array#to_csv over a more traditional
CSV::from_a(arr), other than a few more characters to type.

It's not natural? You're probably returning a string but it looks
like you're asking for a CSV? To make matters worse, performance (in
my limited, completely contrived irb experiments) is slightly worse
with the from_a method.

It is much more
encapsulated. The disadvantage of making Array#to_csv is that you are
modifying a global "variable" (the Array class).

That word doesn't mean what you think it means, I think. That isn't
"more encapsulated".

Even further, you're "modifying 'global variables'" any way you go.
If you add a method to CSV, you're still adding a method somewhere,
except now the API is awkward. So long as you document where the
method is added (whether it's Array or your own CSV class), you're not
hurting anyone. If people using your code hurt themselves because
they don't read documentation, that's their fault. The only
"dangerous" thing I see is perhaps namespace clashes, but in that
case, it's the developer's responsibility to keep track of those.
Even if you use your "encapsulated" version you could still have
another CSV module/class from a from_a method that stomps on that one.

It's a problem anywhere and not using monkeypatching just because
you're afraid of that issue seems a little silly to me. Then again,
maybe my systems (thankfully) haven't been "big" enough to really make
this a concern.

--Jeremy

···

--
http://jeremymcanally.com/
http://entp.com

Read my books:
Ruby in Practice (http://manning.com/mcanally/\)
My free Ruby e-book (http://humblelittlerubybook.com/\)

Or, my blogs:

http://rubyinpractice.com

I think the point of this blog was to discourage people
from using it because it is dangerous. There are simple alternatives.

Why is it dangerous?

···

--
Posted via http://www.ruby-forum.com/\.

Assigning blame to a particular element of a programming language
seems foolish to me. I'm pretty confident I can write some code that
abuses the heck out of while loops. Should we start hating those now
too?

If you read the post, you'll see that I'm not blaming monkey-patching
- I'm blaming the faddish overuse of it when simpler methods would
suffice.

Thanks for the concern, but I did read your article.

Like I said in the post, it's a cultural issue, not a technical one.

I'm just uncomfortable judging an entire community with sweeping stereotypes. I have no idea what you are coding, for example, and thus I don't feel I can speak intelligently about what you should or should not be using.

James Edward Gray II

···

On Feb 25, 2008, at 7:24 AM, Avdi Grimm wrote:

On Sun, Feb 24, 2008 at 11:02 PM, James Gray <james@grayproductions.net > > wrote:

Sorry but you are contradicting yourself. Part of growing in the Ruby
culture is learning the difference between both communities and how
they overlap. I got into Ruby because of Rails and I can tell the
difference very easily. Did it take me some time? Yes. Was it worth
it? Definitely. So, please, don't make it harder for the people that
are new to understand the difference between the communities by
muddling the waters.

AEM

···

On Feb 23, 5:26 pm, Avdi Grimm <a...@avdi.org> wrote:

On Sat, Feb 23, 2008 at 7:13 PM, Marc Heiler <sheve...@linuxmail.org> wrote:
> It is frustrating to try to read a blog that claims monkey patching is
> destroying ruby when in actuality the author meant Rails.

No, I meant ruby. I have been coding in Ruby for ~7 years, since long
before Rails appeared. At work I'm the first to correct people when
they make a comment about "Ruby" when they really are talking about a
Rails-specific feature.

Yes, I used a Rails example. And I suspect that it's true that the
phenomenon is more prevalent in in the Rails community than in the
wider Ruby community. But for better or worse the majority of Ruby
code being written today is being written for Rails, and the Ruby
coders of tomorrow are cutting their teeth in the Rails community.
Rails cultural problems will, increasingly, be Ruby cultural
problems.

--
Avdi

Eivind Eklund wrote:

···

On Sun, Feb 24, 2008 at 2:34 AM, Morton Goldberg > <m_goldberg@ameritech.net> wrote:

On Feb 23, 2008, at 6:56 PM, M. Edward (Ed) Borasky wrote:
> One of my friends once passed this little quotation on to me:
>
> "Always code as if the person who will maintain your code is a
> violent psychopath who knows where you live."

Truly wonderful. If there were a poster of that available, I'd buy it
and hang it where I could see it when I look up from my screen.

You didn't make an attribution, so I presume you don't know who
originated it. Too bad.

It is usually attributed to Martin Golding.

Eivind

I actually first heard it from Martin Golding, and I've forgotten how long ago that was. I did a Google search the other day and quite a few hits came up for Damien Conway.

Could you describe "the general thing" a little bit more? As I
understood it, he wants to limit a "monkey patch" to the scope of a
Module. Which is what I've done. I've only used the import-module
library and added a more user-friendly syntax. I'm not inspecting
caller at all, and import-module doesn't either. What am I missing?

···

2008/2/26, Clifford Heath <no@spam.please.net>:

You can do the special case that Phlip described. You can't do the
general thing which he was exemplifying without doing stupid things
like inspecting caller. I've also done quite a bit of deep metaprogramming
in Ruby, and what he wants (as opposed to his simple example) can't
sensibly be done, and probably shouldn't be anyway. Caller sensitivity
violates POLS.

I don't see the big advantage of Array#to_csv over a more traditional
CSV::from_a(arr), other than a few more characters to type. It is much more
encapsulated.

The big advantage is that, when writing a method that needs to receive
CSV data as an argument, you don't have to do type checking on the
argument - any object that responds correctly to the #to_csv message
can be used.

    def i_need_csv( obj )
      data = obj.to_csv
      do_something_with( data )
    end

This gives more freedom to the user of the library who might have an
object of their own class which knows how to turn itself into CSV
data. Your suggestion would lead to something like:

    def i_need_csv( obj )
      data = case obj
      when Array
        CSV::from_a( obj )
      when Hash
        CSV::from_hash( obj )
      else
        raise "Sorry, I'm not duck-type friendly!"
      end
    end

Yuck.

The disadvantage of making Array#to_csv is that you are
modifying a global "variable" (the Array class).

I'll take that disadvantage (in combination with good test coverage!)
over having to write verbose, type-checking code any day.

···

On Feb 24, 8:57 pm, Eric Mahurin <eric.mahu...@gmail.com> wrote:

--
Regards,

John Wilger

I'd be at least a little interested in potentially offering developers
the chance to 'lock' their classes from monkey patches.

There are probably ways around this, but:

   >> Array.freeze
   => Array
   >> class Array
   >> def to_csv; end
   >> end
   TypeError: can't modify frozen class
     from (irb):7

James Edward Gray II

···

On Feb 26, 2008, at 9:45 AM, Jones, Brian - McClatchy Interactive wrote:

I'd be at least a little interested in potentially offering developers
the chance to 'lock' their classes from monkey patches.

IMHO part of the problem rather is that ruby by its own actually
doesn't provide a way to do this systematically. IIRC there was some
talk about stacked methods in ruby 1.9.x but I personally would rather
vote (if there were a vote) for something simpler like advices as they
are used, e.g., in emacs lisp. No matter what one thinks of elisp,
Emacs is a rather complex system and advices are used in several parts
of the system and IMHO they have proven as a simple yet versatile and
rather robust way to achieve what often is summarized as MP. I suppose
a standardized defadvice would be more robust than the current eval-
or alias-based hacks. And since Emacs is a complex system that makes
heavy use of these techniques, one could probably learn from it.

I know this can be done in pure ruby as it is today rather easily. My
point rather is this should somehow be standardized, i.e. the standard
library should provide standard means to do this.

Regards,
Thomas.

That's ridiculous.

Look. What is the problem? That I might extend a class and you might
extend a class with the same method and thus we can't use each others
code in the same program? Forget overriding core/standard methods --
anyone who does that knows they must do so with SUPER EXTREME caution.
But if someone is writing and end-user application, it doesn't really
matter one way of the other --the code is not intended to be shared.
Extend to your hearts desire. One only needs to be aware of potential
conflicts with 3rd party libs they might use. And for those, in which
the developer is intending for their code to be reusable, then the
developer needs to tread more carefully and follow some simple rules.
Only extend core and standard classes when it's a very clear and
general need. In which case it's a good idea to checkout projects that
attempt to provide some general standardization around such methods
(eg. Facets). It's likely your method is already available, and your
lib's users can have a reasonable basis for knowing what to expect.
Beyond that, if you still really want to extend a core/standard class
in a way vunique to your particular program then give it an equally
unique name --generally putting you project's name as the first part
of the method. For example, the YAML lib adds methods like
#yaml_properties.

By following these rules, getting along with other programs is a
pretty safe bet. And we don't need to add restrictions that will just
make life harder when we do have good reasons to MP.

I should point out one other rule of thumb often overlooked as a
consequence of MP: DO NOT use #respond_to? as a means of determining
if an object is extended by a module or is an instance of some class/
superclass. That's much more likely to lead to unexpected issues with
MPs.

T.

···

On Feb 26, 10:45 am, "Jones, Brian - McClatchy Interactive" <bjo...@mcclatchyinteractive.com> wrote:

I'd be at least a little interested in potentially offering developers
the chance to 'lock' their classes from monkey patches. This could be
useful to the 'core' library that comes with Ruby, and to at least make
developers look at extension points provided via an actual API instead
of just immediately jumping on monkey patching for solving all problems.

You can do this today if you want; all you need to do is freeze the class.

class Foo
   # ... your definitions
end

Foo.freeze # Foo is no longer an open class

-mental

···

On Wed, 27 Feb 2008 00:45:27 +0900, "Jones, Brian - McClatchy Interactive" <bjones@mcclatchyinteractive.com> wrote:

I'd be at least a little interested in potentially offering developers
the chance to 'lock' their classes from monkey patches.

I don't have the book. I wouldn't mind seeing the +/- list.

You are talking about this, right?

Using what this link above suggests (adapter "has-a" or "is-a" adaptee) is
the better way to go, IMO. There are multiple ways to do this in Ruby.
Monkey-patching/open-classes makes the solution: adapter IS adaptee. I
don't know if you can even call it the adapter pattern since you are
destroying the adaptee interface.

···

On Wed, Feb 27, 2008 at 7:46 AM, James Gray <james@grayproductions.net> wrote:

On Feb 24, 2008, at 10:02 PM, James Gray wrote:

> On Feb 23, 2008, at 4:23 PM, Eric Mahurin wrote:
>
>> I usually only do monkey patching in the
>> following cases which have no reuse: top-level script, testing, and
>> quick hacking.
>
> I think it's the ideal tool for adding compatibility methods.

Along these lines, I read a chapter in Design Patterns in Ruby last
night that suggests using Ruby's open classes as one possible way to
implement the Adapter pattern. The book includes a pretty good
discussion of the plusses and minus to this approach and makes
recommendations about when it's probably worth the trade-off. It's a
good read.

James Edward Gray II

As a comparison, the "Design Patterns in Ruby" by Russ Olsen (where MP is one of the Ruby design patterns, in addition to the standard GoF patterns) never suggests reopening classes as a solution to any problem in the book, as far as I remember. When needed, methods are added on a per-object level.

And David Black seem to say about the same thing as the OPs blog, look at page 28 in this presentation:
http://www.chariotsolutions.com/slides/pdfs/rubyeast2007-black-PerObjectBehavior.pdf

Best regards,

Jari Williamsson

This whole point could be moot if each library could have it's own
rendition of (core) classes/modules.

Then with two types of require: one simply to call on a library and
another to fully integrate a library, extensions and all, then we
would have full control over the whole MP affair.

Yes, we can have our cake and eat it too! But it's up to Matz to be
our Marie.

T.

···

On Feb 24, 11:24 am, "Avdi Grimm" <a...@avdi.org> wrote:

On Sun, Feb 24, 2008 at 11:17 AM, Bob Hutchison <hu...@recursive.ca> wrote:
> Aside from that, there isn't a lot to argue with in your blog posting.
> Your points individually are fine and quibbling over insignificant
> details on a mailing list is a waste of time (though it might be ideal
> in a pub :slight_smile: In other words, I don't disagree with anything you say
> until you start drawing conclusions. I *like* monkey patching and I
> approve of its proper use. Furthermore, I don't special case the abuse
> of MP, it is just like any other abuse of a feature: bad.

Thanks. I think the reason I'm making a special case of monkey
patching is that, at least in some subsets of the community, I'm
seeing it morph into almost a standard extension technique. How do
you extend ActiveRecord::Base? Why, you re-open it, of course!
That's how everyone does it!

Aside from that, there isn't a lot to argue with in your blog posting.
Your points individually are fine and quibbling over insignificant
details on a mailing list is a waste of time (though it might be ideal
in a pub :slight_smile: In other words, I don't disagree with anything you say
until you start drawing conclusions. I *like* monkey patching and I
approve of its proper use. Furthermore, I don't special case the abuse
of MP, it is just like any other abuse of a feature: bad.

Thanks. I think the reason I'm making a special case of monkey
patching is that, at least in some subsets of the community, I'm
seeing it morph into almost a standard extension technique. How do
you extend ActiveRecord::Base? Why, you re-open it, of course!
That's how everyone does it!

An analogy maybe... it can be remarkably difficult to get new programmers to use composition rather than inheritance. This is dealt with through education (occasionally a brutal education :slight_smile:

Monkey patching *is* a standard extension technique. It isn't just a hack. At least, that's what I say :slight_smile: If you design a class that is to be used in different contexts then it is reasonable to assume that the class's responsibilities may change with context. This change in responsibility may not be reflected precisely by either inheritance or composition (or forwarding) -- inheritance and composition may be something of a contortion. You can make a pretty strong argument that you shouldn't be abusing inheritance or composition to avoid something neatly expressed by MP. Of course someone can use this interpretation of MP to justify a lot of dubious stuff.

Ruby has other techniques that are not so much talked about that can deal with some of the suspicious uses of MP. These come to mind immediately: dynamically including modules into a class or specific *object* (there are some handy hooks defined in Ruby that allow some really nice stuff to be done when this happens), and methods defined on specific *objects*. These are open to abuse too, and can be spectacularly difficult to debug if you are thinking that an object is defined by its class.

This could be more of a Rails thing at the moment, but as I commented
earlier, Rails culture will increasingly become Ruby culture, because
that's where the new Ruby programmers are coming from.

I agree I think. I believe that it is very important to distinguish between a language's capability to do something and its use/abuse. The trouble with Rails, and its tremendous success, is that less experienced programmers can get a *long* way before they really need to know anything about programming. Somewhere in that interval is room for some pretty ugly code -- it isn't just MP.

Cheers,
Bob

···

On 24-Feb-08, at 11:24 AM, Avdi Grimm wrote:

On Sun, Feb 24, 2008 at 11:17 AM, Bob Hutchison <hutch@recursive.ca> > wrote:

--
Avdi

----
Bob Hutchison -- tumblelog at http://www.recursive.ca/so/
Recursive Design Inc. -- weblog at http://www.recursive.ca/hutch
http://www.recursive.ca/ -- works on http://www.raconteur.info/cms-for-static-content/home/

One of the reasons concepts like modules and classes were introduced
to programming languages was to reduce accidental collisions. Monkey
patching discards all of that. In a sufficiently large project where
monkey patching isn't vigorously discouraged, unexpected and extremely
difficult to debug interactions are almost inevitable, because monkey
patches by different authors (or by the same author at different
times) interact or collide in unpredictable ways.

Where this becomes a community problem and not just a team problem is
when authors of gems and plugins start to see monkey patching as "the"
way to write extensions in Ruby. Thus making such unpredictable
interactions practically unavoidable if you want to re-use anyone
else's code.

···

On Mon, Feb 25, 2008 at 12:33 AM, Marc Heiler <shevegen@linuxmail.org> wrote:

Why is it dangerous?

--
Avdi