Proposed Solutions - Was [ Re: Monkeypatching is Destroying Ruby ]

James, Sometime ago Rick Olson wrote this:

http://weblog.techno-weenie.net/2007/12/19/adventures-in-rails-debugging

Does ring a bell, doesn't it? All the libraries Rick mentions in his
blog are well regarded and yet he has a problem.

times and you know these problems are HARD to debug.

As i wrote earlier, today you have access to source code of all the
libraries you are using because Ruby is source based interpreter and
you can still debug problems because of accidental overrides or
modifying core classes by looking at source code, but what if Rubinius
or IronRuby becomes defacto and people started distributing compiled
bytecodes?

I propose a small change in the language and perhaps I know it won't
see light of the day, but how about this?

class Array
  def to_csv #> works if in final program no one is actually doing the same
  end
end

However, suppose you use another library and they have done the same,
that code won't run and Ruby will throw up and that sorta makes
sense.However, if you are sure of what you are doing, you can go ahead
and write:

class Array
  def! to_csv #=> similar to override keyword in other languages
  end
end

Ditto, if you want to override methods defined in other classes, you
must use "def!" .

I know this is somewhat impractical because Ruby has grown into such a
beast, lots of standard library classes and gems will need small
modification ( but we are in midst of such modification anyways
courtesy of 1.9.x ).

My point is, I love open classes, but it shouldn't hurt to have little
bit of safety precaution built into the language, does it?

···

On Tue, Feb 26, 2008 at 11:55 PM, James Britt <james.britt@gmail.com> wrote:

Trans wrote:
>
> 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.
>
> That's ridiculous.

Fears about open classes sound very much like what people say about
dynamic typing.

"#{@core_feature} is unpredictable!"

"#{@core_feature} creates problems that cannot be found until run-time!"

"#{@core_feature} is unsafe!"

"#{@core_feature} should be locked down!"

It's not that these claims are entirely untrue, it's just that, in real
life, most people simply do not encounter the alleged problems.

Code that is poorly written or does not play well with others tends to
get discarded.

From my own experience I have encountered similar problems countless

I'm with James on this, the wins from Ruby's flexibility far out-weigh
the downsides.

On the other hand, I don't think I've seen a suggestion quite like
this before. It's pretty neat actually I think. Like a poor-man's
selector-namespaces.

Now if we ever did get selector-namespaces, then I'd be completely
against this. But since it appears we're _not_ getting the feature,
this isn't a half-bad substitute IMO.

Except for the "ruby throwing up" part. Collissions should be
warnings, not fatal errors. Otherwise, like you said, you eventually
may not have the source for a lib, so you wouldn't have a choice but
to abandon it or hack in something like:

require 'faster_csv'
Array.send(:undefine_method, :to_csv)

I'm not saying this def! idea is really needed... but I do thing it's
pretty novel an idea, and I don't see any downsides.

···

On Feb 28, 8:39 am, hemant <gethem...@gmail.com> wrote:

On Tue, Feb 26, 2008 at 11:55 PM, James Britt <james.br...@gmail.com> wrote:
> Trans wrote:

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

> > That's ridiculous.

> Fears about open classes sound very much like what people say about
> dynamic typing.

> "#{@core_feature} is unpredictable!"

> "#{@core_feature} creates problems that cannot be found until run-time!"

> "#{@core_feature} is unsafe!"

> "#{@core_feature} should be locked down!"

> It's not that these claims are entirely untrue, it's just that, in real
> life, most people simply do not encounter the alleged problems.

> Code that is poorly written or does not play well with others tends to
> get discarded.

James, Sometime ago Rick Olson wrote this:

http://weblog.techno-weenie.net/2007/12/19/adventures-in-rails-debugging

Does ring a bell, doesn't it? All the libraries Rick mentions in his
blog are well regarded and yet he has a problem.

From my own experience I have encountered similar problems countless
times and you know these problems are HARD to debug.

As i wrote earlier, today you have access to source code of all the
libraries you are using because Ruby is source based interpreter and
you can still debug problems because of accidental overrides or
modifying core classes by looking at source code, but what if Rubinius
or IronRuby becomes defacto and people started distributing compiled
bytecodes?

I propose a small change in the language and perhaps I know it won't
see light of the day, but how about this?

class Array
def to_csv #> works if in final program no one is actually doing the same
end
end

However, suppose you use another library and they have done the same,
that code won't run and Ruby will throw up and that sorta makes
sense.However, if you are sure of what you are doing, you can go ahead
and write:

class Array
def! to_csv #=> similar to override keyword in other languages
end
end

Ditto, if you want to override methods defined in other classes, you
must use "def!" .

I know this is somewhat impractical because Ruby has grown into such a
beast, lots of standard library classes and gems will need small
modification ( but we are in midst of such modification anyways
courtesy of 1.9.x ).

My point is, I love open classes, but it shouldn't hurt to have little
bit of safety precaution built into the language, does it?

My point is, I love open classes, but it shouldn't hurt to have little
bit of safety precaution built into the language, does it?

In the mean time, a quick hack: http://pastie.caboo.se/paste/158825

Example:

load 'redefine.rb'

=> true

?> class Module

    include Redefine
  end

=> Module

?> class Monkey

    def ook_ook
        puts "ook ook!"
load 'redefine.rb'

=> true

?> class Module

    include Redefine
  end

=> Module

?> class Monkey

    def ook_ook
        puts "ook ook!"
      end
  end

=> nil

?> class Monkey

    redefine_method(:ook_ook) { "please, may I have another banana?" }
  end

Redefine::MethodRedefinitionError: Method 'ook_ook' redefined
        from (irb):14

?> Monkey.new.ook_ook
ook ook!
=> nil

?> Redefine.allow_collisions= :warn
=> :warn

?> class Monkey

    redefine_method(:ook_ook) { "please, may I have another banana?" }
  end

Method 'ook_ook' redefined at (irb):22:in `irb_binding'
=> #<Proc:0x00624224@(irb):22>

?> Monkey.new.ook_ook
=> "please, may I have another banana?"

?> Redefine.allow_collisions= true
=> true

?> class Monkey

    redefine_method(:ook_ook) { "Look out, I'm in a poo-flinging mood" }
  end

=> #<Proc:0x004b0410@(irb):30>

?> Monkey.new.ook_ook
=> "Look out, I'm in a poo-flinging mood"

···

On Thu, Feb 28, 2008 at 9:39 AM, hemant <gethemant@gmail.com> wrote:

--
Avdi

I think there is deep misunderstanding about all this. The main issue
is really that Rails has added so many extensions to Ruby that
inexperienced Rails programmers follow suit and extend Ruby willy-
nilly. But that's not Ruby's problem. Ruby is an open "agile" language
--it lets you override almost any part of the system. Yes, that can be
"dangerous", but it can also be extremely powerful. It's up to the
programmer to consider their usecase and make the call. No amount of
preventative obstruction is going to make anything better, it will
only make life more difficult for those who use MP appropriately. I
personally think Ruby has too many meta-obstructions as it is. I
certainly don't want more.

If we can come up with a real solution --eg. a good selector
namespaces design, then great. But short of that there is no point.
You can already do this:

  $ cat temp.rb
  class String
    def to_s; ""; end
  end

  $ ruby -w temp.rb
  temp.rb:2: warning: method redefined; discarding old to_s

That ought to be preventive care enough.

T.

···

On Feb 28, 9:39 am, hemant <gethem...@gmail.com> wrote:

James, Sometime ago Rick Olson wrote this:

http://weblog.techno-weenie.net/2007/12/19/adventures-in-rails-debugging

Does ring a bell, doesn't it? All the libraries Rick mentions in his
blog are well regarded and yet he has a problem.

From my own experience I have encountered similar problems countless
times and you know these problems are HARD to debug.

As i wrote earlier, today you have access to source code of all the
libraries you are using because Ruby is source based interpreter and
you can still debug problems because of accidental overrides or
modifying core classes by looking at source code, but what if Rubinius
or IronRuby becomes defacto and people started distributing compiled
bytecodes?

I propose a small change in the language and perhaps I know it won't
see light of the day, but how about this?

class Array
  def to_csv #> works if in final program no one is actually doing the same
  end
end

However, suppose you use another library and they have done the same,
that code won't run and Ruby will throw up and that sorta makes
sense.However, if you are sure of what you are doing, you can go ahead
and write:

class Array
  def! to_csv #=> similar to override keyword in other languages
  end
end

Ditto, if you want to override methods defined in other classes, you
must use "def!" .

I know this is somewhat impractical because Ruby has grown into such a
beast, lots of standard library classes and gems will need small
modification ( but we are in midst of such modification anyways
courtesy of 1.9.x ).

My point is, I love open classes, but it shouldn't hurt to have little
bit of safety precaution built into the language, does it?

Topmost-level code could set up callbacks before the 'require' lines
for the needed libs. It would be the client's responsibility to check
for changes and adapt to them.

SingletonListener.new(Array) { |singleton|
   singleton # => Array

   {
      :reopened_begin => lambda {
         # Encountered 'class Array' line ... probably only useful for
thread sync
         # ...
      },
      :reopened_end => lambda {
         # Encountered corresponding 'end' for the above 'class Array'
         # ...
      },
      :method_added_begin => lambda { |method_name|
         # Someone has begun adding a method, either with the line
         # 'def foo' or define_method or otherwise
         method_name # => String
         # ...
      },
      :method_added => lambda { |method|
         # Forwarded from Module's method_added
         method.class # => UnboundMethod
      },
      :method_removed => lambda { |method|
         # Forwarded from Module's method_removed
         # ...
      },
      :const_added => lambda { |const_name, value|
         # ...
      },
      :const_removed => lambda { |const_name, value|
         # ...
      },

      # ... other callbacks ...
   }
}

For example I plan to define Array#foo, but I get a callback from the
library when it defines foo. After I've 'require'd everything I need,
but before I've required my libs, I could run a unit test on the
existing foo. If the results are identical to the test results of my
implementation, I'd perhaps leave it alone. If not, then at least I
know.

This could all be done without SingletonListener, but the idea is to
have a common idiom which people recognize and expect (if not
SingletonListener then something else).

--FC

hemant wrote:

James, Sometime ago Rick Olson wrote this:

http://weblog.techno-weenie.net/2007/12/19/adventures-in-rails-debugging

Does ring a bell, doesn't it?

No. Why should it?

I try to avoid Rails.

Too annoying.

...
My point is, I love open classes, but it shouldn't hurt to have little
bit of safety precaution built into the language, does it?

Ah HA! I *knew* it!

It's the static typing permathread in disguise.

···

--
James Britt

"Tear it up and start again."
  - Anonymous

Since we've been using FasterCSV's extension to Array, I just wanted to point out some small facts in it's defense:

* FasterCSV does support the alternative syntax: FCSV.generate_line(array)
* Array#to_csv just wraps the above call, so there's no danger of losing functionality if a collision occurs

In summary, Array#to_csv is provided just as a convenience to developers. You are free to ignore it.

James Edward Gray II

···

On Feb 28, 2008, at 8:59 AM, Sam Smoot wrote:

Otherwise, like you said, you eventually
may not have the source for a lib, so you wouldn't have a choice but
to abandon it or hack in something like:

require 'faster_csv'
Array.send(:undefine_method, :to_csv)

Hi,

I'm with James on this, the wins from Ruby's flexibility far out-weigh
the downsides.

I am with James on this as well. Open Classes is a feature and as I
said I love it.

On the other hand, I don't think I've seen a suggestion quite like
this before. It's pretty neat actually I think. Like a poor-man's
selector-namespaces.

Now if we ever did get selector-namespaces, then I'd be completely
against this. But since it appears we're _not_ getting the feature,
this isn't a half-bad substitute IMO.

I am with you here. If we can get selector-namespaces, we wouldn't
need this. Although in case of overriding method
defined in parent class, 'def!' could still be useful.

···

On Thu, Feb 28, 2008 at 8:29 PM, Sam Smoot <ssmoot@gmail.com> wrote:

Except for the "ruby throwing up" part. Collissions should be
warnings, not fatal errors. Otherwise, like you said, you eventually
may not have the source for a lib, so you wouldn't have a choice but
to abandon it or hack in something like:

require 'faster_csv'
Array.send(:undefine_method, :to_csv)

I'm not saying this def! idea is really needed... but I do thing it's
pretty novel an idea, and I don't see any downsides.

--
Let them talk of their oriental summer climes of everlasting
conservatories; give me the privilege of making my own summer with my
own coals.

http://gnufied.org

Well, while I'm sitting here thinking about it, let's try another
concept for selector namespaces:

  class Array
    def to_csv for FasterCSV
      # ...
    end
  end

  class String
    def to_a for FasterCSV
  end

  "".to_a # will use Ruby's
  .to_csv # NoMehtodError

  space FasterCSV

  "".to_a # will use FasterCSV's

Anything defined within FasterCVS will use the FasterCSV space by
default, in which case you'd have to say "space Ruby" to get access to
pure.

Space selections are reset per file.

T.

···

On Feb 28, 9:59 am, Sam Smoot <ssm...@gmail.com> wrote:

Now if we ever did get selector-namespaces, then I'd be completely
against this. But since it appears we're _not_ getting the feature,
this isn't a half-bad substitute IMO.

Hi Trans,

I think there is deep misunderstanding about all this. The main issue
is really that Rails has added so many extensions to Ruby that
inexperienced Rails programmers follow suit and extend Ruby willy-
nilly. But that's not Ruby's problem. Ruby is an open "agile" language
--it lets you override almost any part of the system. Yes, that can be
"dangerous", but it can also be extremely powerful. It's up to the
programmer to consider their usecase and make the call. No amount of
preventative obstruction is going to make anything better, it will
only make life more difficult for those who use MP appropriately. I
personally think Ruby has too many meta-obstructions as it is. I
certainly don't want more.

Rails has increased Ruby's mind share significantly and perhaps first
time Ruby is being used to built applications that are really complex
and large. For example, I do significant work in Ruby without Rails
and my Rails project size is currently 71498 lines ( including
plugins that I am using, but not the framework and not counting
Javascript, rhtml,css,xml,yaml ).

Too often, I have seen people blaming everything to Rails, but I do
not think its true in this case.
In the first link that I posted, whose fault it is? I have 103 gems
installed on my machine, and not counting
Rails libraries and facet , I still have around 5 or 6 of them, which
are modifying core classes one way or the other.

Trans, often on #ruby-lang I have heard from experienced rubyists why
they avoid facet. I do not think, its a problem of rails.
Ruby has grown and is being used by people with varying skills. I do
not think, features that help people in avoiding these problems is
bad.

As a last note, I will leave you with this:

http://blog.brightredglow.com/2008/1/17/evil-can-be-dangerous

So, no its not specific to Rails. Its not a problem either, but we can
do better.

···

On Thu, Feb 28, 2008 at 10:57 PM, Trans <transfire@gmail.com> wrote:

If we can come up with a real solution --eg. a good selector
namespaces design, then great. But short of that there is no point.
You can already do this:

  $ cat temp.rb
  class String
    def to_s; ""; end
  end

  $ ruby -w temp.rb
  temp.rb:2: warning: method redefined; discarding old to_s

That ought to be preventive care enough.

T.

--
Let them talk of their oriental summer climes of everlasting
conservatories; give me the privilege of making my own summer with my
own coals.

http://gnufied.org

In the mean time, a quick hack: http://pastie.caboo.se/paste/158825

Example:

Avdi, sorry but it isn't any solution for problem. You get warning or
exception and method won't be overriden. Imagine that you want use 2
libraries that collide and you get sach a error. Is it usefull?
Another problem is that there are at least two cases why you are
opening existing class and adding features to them:
#1 You open class to just add new method and your library depend on it
for example
class String
  def underscore
    #...
  end
end

When other library do the same there is problem because first library
use diffretent method than defined by itself.

#2 You open class to knowingly override existing method to change/enhance it
class ActiveRecord::Base
  def find(*args)
    #...
  end
end

The same problem here: if other library/plugin/whatever do the same
after our change then we have problem.

James, Sometime ago Rick Olson wrote this:

http://weblog.techno-weenie.net/2007/12/19/adventures-in-rails-debugging

Great story. My opinion is rails and many other libraries use too much
MP. This story shows it. Opening String class just to add stupid
"underscore" method? So evil.

···

On Thu, Feb 28, 2008 at 6:10 PM, Avdi Grimm <avdi@avdi.org> wrote:

--
Radosław Bułat

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

Now if we ever did get selector-namespaces, then I'd be completely
against this. But since it appears we're _not_ getting the feature,
this isn't a half-bad substitute IMO.

I made a suggestion for this a few hours ago. Looks like it got lost
in cyberspace :frowning: Oh well, I'll be extra brief this time, but
reiterate the idea:

  class Array
    def to_csv for FasterCSV
      #...
    end
  end

  .to_csv #=> NoMethodError

  space FasterCSV

  .to_csv #=> useses FasterCSV's version

  space Ruby

  .to_csv #=> NoMethodError

Anything defined in FasterCSV would automatically use FasterCSV space
(use 'space Ruby' to get pure space).

T.

hemant wrote:

James, Sometime ago Rick Olson wrote this:
http://weblog.techno-weenie.net/2007/12/19/adventures-in-rails-debugging
Does ring a bell, doesn't it?

No. Why should it?

I try to avoid Rails.

Too annoying.

Well, Rails internals are truly gruesome sometimes. I just had a long session trying to change some behaviour in the routing system. There are multiple real wtfs in it... well, back to topic.

...
My point is, I love open classes, but it shouldn't hurt to have little
bit of safety precaution built into the language, does it?

Ah HA! I *knew* it!

It's the static typing permathread in disguise.

Hehe, it may be... i don't think we that another language construct helps. As some have illustrated, all those problems can be solved with discipline on both sides (authors and users). If i wanted a language construct for everything, i'll head back to Java || C++.

The only solution that would be an addition to the language would be real selector namespaces.

Greetings
Skade

···

On Feb 29, 2008, at 11:29 PM, James Britt wrote:

James, no offense intended. In my book FasterCSV is just about the
most useful Ruby lib of all time. :slight_smile:

···

On Feb 28, 10:15 am, James Gray <ja...@grayproductions.net> wrote:

Since we've been using FasterCSV's extension to Array, I just wanted
to point out some small facts in it's defense:
James Edward Gray II

Trans wrote:

Now if we ever did get selector-namespaces, then I'd be completely
against this. But since it appears we're _not_ getting the feature,
this isn't a half-bad substitute IMO.

Well, while I'm sitting here thinking about it, let's try another
concept for selector namespaces:

  class Array
    def to_csv for FasterCSV
      # ...
    end
  end

  class String
    def to_a for FasterCSV
  end

  "".to_a # will use Ruby's
  .to_csv # NoMehtodError

  space FasterCSV

  "".to_a # will use FasterCSV's

Anything defined within FasterCVS will use the FasterCSV space by
default, in which case you'd have to say "space Ruby" to get access to
pure.

Space selections are reset per file.

Could even do this now, using semi-obscure method names instead of namespaces:

class Array
   def to_csv_for_FasterCSV
     # ...
   end
end

class FasterCSV
   def from obj # class name of obj isn't embedded in method name!
     obj.to_csv_for_FasterCSV
   end
end

a = [1,2,3]
csv = FasterCSV.from(a)

We don't have to switch on the type of a to call the right FasterCSV method (or switch on the type of a to define a singleton #to_csv method).

The chance of collision is greatly reduced by explicit naming.

The main disadvantage compared with Trans's suggestion is that within FasterCSV you have to use longer method names.

But the "public" API is kept clean because you use FasterCSV.from rather than #to_csv_for_xxx.

···

On Feb 28, 9:59 am, Sam Smoot <ssm...@gmail.com> wrote:

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

I like this one. I see two issues that still needs to be resolved:

1. How to get at several spaces.
2. What's the speed penalty involved?

Eivind.

···

On Thu, Feb 28, 2008 at 9:02 PM, Trans <transfire@gmail.com> wrote:

> Now if we ever did get selector-namespaces, then I'd be completely
> against this. But since it appears we're _not_ getting the feature,
> this isn't a half-bad substitute IMO.

I made a suggestion for this a few hours ago. Looks like it got lost
in cyberspace :frowning: Oh well, I'll be extra brief this time, but
reiterate the idea:

  class Array
    def to_csv for FasterCSV
      #...
    end
  end

  .to_csv #=> NoMethodError

  space FasterCSV

  .to_csv #=> useses FasterCSV's version

  space Ruby

  .to_csv #=> NoMethodError

Anything defined in FasterCSV would automatically use FasterCSV space
(use 'space Ruby' to get pure space).

Responding to Trans:

  class Array
    def to_csv for FasterCSV
      #...
    end
  end

  .to_csv #=> NoMethodError

  space FasterCSV

  .to_csv #=> useses FasterCSV's version

  space Ruby

  .to_csv #=> NoMethodError

Looks somewhat similar to the "context-oriented programming" approach.

http://www.swa.hpi.uni-potsdam.de/cop/

Best regards,
Patrick