Monkeypatching is Destroying Ruby

J

Jeremy McAnally

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. :)
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://mrneighborly.com
http://rubyinpractice.com
 
C

Clifford Heath

Phlip said:
If so, and if it "can't be done in Ruby", why are there Ruby gems that talk
about AOP?

Because what's called AOP isn't. There's always a better way
than the injection of code which is referred to as AOP, IMO.
I could argue this at length, but I hope that not many people
here actually care :).
That's probably the Ruby AOP packages. Thanks for the pointers!

If you want to stay in Ruby, you should use traits instead. It's a
little weaker than what I'd call "proper" AOP, but gets as close as
you can meaningfully get.


Clifford Heath.
 
E

Eivind Eklund

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
 
A

Avdi Grimm

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. Like I said in the post, it's a cultural issue, not a
technical one.
 
A

Avdi Grimm

Why is it dangerous?

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

James Gray

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
 
E

Eric Mahurin

[Note: parts of this message were removed to make it a legal post.]

Why is it dangerous?


It is equivalent to modifying global variables. Modifying globally
accessible classes (not just meta-classes) should be exception, not the
rule.

matz also agrees that monkey patching is dangerous. Here are a few quotes:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/172172

"open class" is so strong (often too strong), we can break things easily. In
other word, Ruby trust you to give you sharp knives, where Python don't.
From the Python point of view, it's wrong, I guess.

http://www.rubyist.net/~matz/slides/rc2005/mgp00031.html

- Open class is Too Dangerous
- Global Modification

Until there is a "safe" way (i.e. namespaces) for monkey patching (opening
global classes), I think it should be discouraged. Discouraged, not
banned. It still is a tool that can be used, but it shouldn't be the first.
 
A

aemadrid

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.

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
 
M

Michal Suchanek

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

You know, you can interpret this both ways. Either write your code so
trouble-free that he never notices it or make it such that he gets a
heart attack the moment he tries to to look at it/use it.

Of course depending on the thing you write one approach may be more
likely to succeed than the other ..

Thanks

Michal
 
M

M. Edward (Ed) Borasky

Eivind said:
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.
 
P

Pit Capitain

2008/2/25 said:
It can't be done in Ruby. (...)

I would be *very* careful with sentences like this. Remember, this is
Ruby. This topic has been discussed more than once on the mailing
list. For example look at ruby-talk:244014. Here's the example Phlip
talks about:

require "import-module-extended"

module MyModule
extending(String) do
def inspect
return 'shock the monkey'
end
end
def call_show(str)
show(str)
end
end

class MyClassA
def call_show(str)
show(str)
end
def show(str)
p str
end
end

class MyClassB
include MyModule
def show(str)
p str
end
end

MyClassA.new.call_show("hi") # => "hi"
MyClassB.new.call_show("hi") # => shock the monkey

Regards,
Pit

PS: I can't find Phlip's posts in the ruby-talk archives...
 
B

Bill Kelly

From: "Pit Capitain said:
PS: I can't find Phlip's posts in the ruby-talk archives...

I don't know if this will turn out to be related or not, but
something similar happened to me. My posts are missing from
the ruby-talk archives for a period of over a year (October
2006 - November 2007.) They do show up in a Google groups
search. (I post directly to the mailing list though, not to
the newsgroup.)


Regards,

Bill
 
C

Clifford Heath

Pit said:
I would be *very* careful with sentences like this.

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

John Wilger

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

Pit Capitain

2008/2/26 said:
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.

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?
 
J

Jones, Brian - McClatchy Interactive

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.

Brian
 
J

James Gray

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:
TypeError: can't modify frozen class
from (irb):7

James Edward Gray II
 
J

Jari Williamsson

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

IMO, this is not an either/or case. Why not just add to_csv() only to
the instance who actually could need it?

To me, a big part of the duck typing concept is to define an object for
what it really describes, not just lazily include every method you might
or might not need.


Best regards,

Jari Williamsson
 
T

ThoML

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,773
Messages
2,569,594
Members
45,121
Latest member
LowellMcGu
Top