Monkeypatching is Destroying Ruby

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?

On Sun, Feb 24, 2008 at 11:02 PM, James G. [email protected]
wrote:

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.

On Sun, Feb 24, 2008 at 2:34 AM, Morton G.
[email protected] wrote:

originated it. Too bad.
It is usually attributed to Martin Golding.

Eivind

On Mon, Feb 25, 2008 at 12:33 AM, Marc H. [email protected]
wrote:

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.

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

On Sun, Feb 24, 2008 at 11:02 PM, James G. <[email protected]

wrote:
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 G. II

On Sun, Feb 24, 2008 at 11:33 PM, Marc H. [email protected]
wrote:

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?

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.

On Feb 23, 5:26 pm, Avdi G. [email protected] wrote:

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

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

Phlip wrote:

http://avdi.org/devblog/?p=18
I don’t know what “Aspect Oriented Programming”…

I want this:
(clip)
I want .inspect, or whatever, outside my module, to behave normally. But if
you inspect a string while my module is above you on the call stack, I get
my hotwired version of .inspect.
Does anyone have a Ruby Hack which does that yet? How hard would it be?

It can’t be done in Ruby. I spent quite a lot of time analysing how to
construct such a language, as I’ve used this kind of design principle
for more than a decade to design aspect-oriented object models.

The problem is that for every call, you need to pass in a hidden
parameter
which contains a descriptor for every namespace visible from the
caller’s
perspective, and you need to use that list of namespaces to disambiguate
the method call itself. Access to other parameters of the method from
inside the callee is also a problem, as they may be of classes that
“don’t
exist” from the POV of the callee.

Basically, such a language is possible, but I don’t believe the
implementation
can be made efficient.

A more restricted version of this behaviour can be done by treating
every
aspect-limited extension (which I call a facet) of an object, as a
special
kind of subclass. Such a subclass must not be allowed private access; it
can only use the public interface of its superclass. The superclass
instance
is initially instantiated as just the superclass, but the facet methods
are
“realized” as they’re used, revealing the aspect behaviour of the
object.
Again, this can’t be faked very well in Ruby, and the various Traits
modules
are a better solution.

However, I do believe that from a modeling POV, the approach is very
valuable.
My ActiveFacts project explores the only real way to do this
effectively,
which is in terms of elementary fact types. That means that all roles of
each object type are specified in one or more vocabularies (aspects),
and
since the fact types are elementary, they’re totally composable.

The same approach isn’t easy to extend to modeling behaviour, for the
reasons I mentioned. You say tomato, I say tomatoe, but in Ruby, the
composition of the two things can only have one name, or you get
collisions.
Unless you want to modify a Ruby interpreter to allow you to
monkey-patch
Method#call, but then you’re implementing your own VM :-).

Clifford H…

Phlip wrote:

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

A more restricted version of this behaviour can be done by treating every
aspect-limited extension (which I call a facet) of an object, as a special
kind of subclass.
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 H…

Eivind E. wrote:

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.

2008/2/25, Clifford H. [email protected]:

Phlip wrote:

I want .inspect, or whatever, outside my module, to behave normally. But if
you inspect a string while my module is above you on the call stack, I get
my hotwired version of .inspect.
Does anyone have a Ruby Hack which does that yet? How hard would it be?

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…

From: “Pit C.” [email protected]

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

Pit C. wrote:

2008/2/25, Clifford H. [email protected]:

Phlip wrote:

I want .inspect, or whatever, outside my module, to behave normally. But if
you inspect a string while my module is above you on the call stack, I get
my hotwired version of .inspect.
Does anyone have a Ruby Hack which does that yet? How hard would it be?

It can’t be done in Ruby. (…)

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.

2008/2/26, Clifford H. [email protected]:

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?

On 24/02/2008, M. Edward (Ed) Borasky [email protected] wrote:

troubled about the popularization of monkey patching in the Ruby

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

On Feb 24, 8:57 pm, Eric M. [email protected] wrote:

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.


Regards,

John W.

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.

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

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

John W. wrote:

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.

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

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.