News for hackers. Stuff that matters.
Re:I'm Sick of This by tdavis on Wednesday April 08, 2009 @12:48PM (#552964)
It disgusts me on levels I cannot describe that this is standard operating procedure for ruby modules (gems, etc) and is the main reason I could never use it. Adding and replacing methods on builtin classes? Are these people out of their minds?! I don't understand it. I've used monkey patches once or twice and it always feels really dirty.
Re:I'm Sick of This by blasdel on Wednesday April 08, 2009 @02:21PM (#553152)
Ruby, despite having 4? sigils as scoping operators, pervasively screws up or simply doesn't have support for scoping.

It really should have support for having class modifications scoped to your module (and accessible manually from outside), but I couldn't find it.

If the language is such that everything "should" be a method on the broadest-possible class, people are going to monkeypatch, because it feels "wrong" otherwise. People just want to write idiomatic code.

In Objective-C this ends up not being an issue, because there's very little usage of non-Apple libraries -- plus the default way of adding methods doesn't allow replacement, and Apple libraries are well-versioned (if you build your app for >10.4, API changes in 10.5 are invisible).

Re:I'm Sick of This by akkartik on Wednesday April 08, 2009 @06:49PM (#553556)
_why's done some work on this: http://github.com/why/potion/tree/master

     Scoped mixins. Basically, you can modify
     object behavior within a scope. So changes
     to core classes can be localized.
     ++ INCOMPLETE ++
Re:I'm Sick of This by hboon on Thursday April 09, 2009 @05:37AM (#554159)
Do you mean to say the default way of adding methods to existing Objective C classes can't replace existing methods? That's not true. I just hit it recently. Subtle bug.
Re:I'm Sick of This by stewiecat on Wednesday April 08, 2009 @01:12PM (#553009)
It's the shiny new toy every new ruby dev wants to play with. Once they get bit in the butt by some really obscure bug that requires the correct combination of 10 gems to reproduce they'll realize that maybe monkeypatching isn't such a hot idea, until then we'll have a bazillion versions of String.
Re:I'm Sick of This by raganwald on Wednesday April 08, 2009 @01:27PM (#553041)
I encourage Ruby programmers to monkey-patch in their "leaf projects." If you want to write Array#sum for your web app, I say go ahead, it's your butt and you are old enough to deal with the consequences. However, when writing gems, plugins, frameworks, any code that others will use is a different story: Now your "clients" suffer the consequences of your choices.

A story about how a web app was broken by installing the Classifier gem due to an Array#sum conflict wouldn't be a story. You change your web app and move along smartly. But who in tarnation is up for patching gems to deal with the conflicts caused by gems patching Array on each other. Ugh.

Re:I'm Sick of This by dasil003 on Wednesday April 08, 2009 @04:26PM (#553370)
This very consideration led me to redefine nil? on an inner class in my SafeNestedHash gem. It's definitely ugly and weird, but I needed a method that existed on object so I can guarantee it's everywhere. Any other thoughts on how to solve this problem?

http://github.com/dasil003/safe-nested-hash/tree/master

Re:I'm Sick of This by pdubroy on Wednesday April 08, 2009 @05:46PM (#553485)
Why not just define a function in your code, rather than redefining 'nil?'?
Re:I'm Sick of This by dasil003 on Wednesday April 08, 2009 @07:03PM (#553585)
It's a kludge so that people don't have to write:

  if hash[:a][:long][:key][:chain].is_a?(SafeNestedHash::UndefinedHash)
Instead they can just say:

  if hash[:a][:long][:key][:chain].nil?
If the nested hash IS defined it could be any type under the sun, therefore a method has to be defined on object, and nil? seemed the only reasonable choice.
Re:I'm Sick of This by tdavis on Wednesday April 08, 2009 @10:44PM (#553818)
Maybe I don't really understand Ruby, but isn't it basically a duck-typed language? Why would you be checking the type at all (which I am assuming `is_a?` does)? Type checking is evil for weakly-typed languages. Either it implements what you need it to implement or has some property value, otherwise it should throw an exception.

What are you testing for here? To see if the hash is empty or something? What makes an "UndefinedHash" undefined? If it IS defined, then it should be assumed to be the type of object you are looking for until the code tries to do something that breaks on a non-supported type.

Re:I'm Sick of This by dasil003 on Wednesday April 08, 2009 @11:41PM (#553884)
The purpose of the SafeNestedHash is to be able to directly reference or assign deep nested hashes without initializing every level. Ruby will throw an error if you do this with a regular hash because the first undefined level returns nil and nil does not respond to the [] method (square brackets are just a method in Ruby). The UndefinedHash is a class that stands in for nil so we don't get an error as we make a deep reference and it also keeps track of the keys so that if there is an assignment at the end it can initialize the necessary sub-hash and attach it to the original top-level hash. There are simpler hacks around that auto-initialize a hash, but they suffer from auto-vivification of hash keys (ie. looking up a hash key will create an empty hash there even without an assignment).

In essence, the problem could be "solved" by monkey patching nil, though that would be the worst sort of abuse of monkey patching I can imagine. As far as checking the class is concerned, well that's just a convenient method of showing what I'm doing, I could also define a method such as is_not_defined? and check if the object at the end of the hash responds to that. The fundamental issue is that any object could be stored in the hash, so whatever I check has to be valid for any possible object. That's why I defined nil? for the UndefinedHash class even though it is a bit kludgey--it's better than monkey patching Object or NilClass.

Re:I'm Sick of This by tdavis on Thursday April 09, 2009 @12:22AM (#553921)
Ruby will throw an error if you do this with a regular hash

So, why not just let it throw an error? The interpreter is expecting one thing and gets another -- that's a type error right there. Why bother with anything else?

(ie. looking up a hash key will create an empty hash there even without an assignment)

That's pretty silly ;)

Re:I'm Sick of This by dasil003 on Thursday April 09, 2009 @01:49PM (#554783)
Now you're asking for the entire justification of my class to exist.

Say there is an algorithm you want to run where you are grouping things into a deeply nested set of categories. A nested hash referencing arrays is a good data structure for this. In PHP this is trivial (though not terribly efficient). In Ruby you have to deal with maintaining the hash. There are many ways to do it, but my SafeNestedHash class lets you do it with a concise, natural syntax. Perhaps if you have an aversion to using a library for this then you would define a pair of methods like key_exists?(hash, key_chain) and set_key(hash, key_chain). You could also monkey-patch hash to have those methods. You could also encapsulate the logic into an object that handles all the logic. Or (as you seem to suggest) you could let the interpreter throw an exception and then handle it with rescue, which IMO would be just a notch behind monkey-patching in terms of bad practice since exceptions should be for unexpected circumstances. I just happened to go with creating a class that abstracts this all away. Yes there is a bit of leakiness, but there is no monkey patching, and any other solution would require either ballooning the actual function with incidental accounting details, or else utilizing some other abstraction which would need to be used with the same understanding of its purpose, at which point it's just a matter of taste.

Re:I'm Sick of This by tdavis on Thursday April 09, 2009 @11:55PM (#555669)
I think you're misinterpreting exceptions in so much that you feel they need to be unexpected. Say you have a list like [[1,2,3],[4,5,6]]. You could count each of the lists and loop through them individually, or you could create a recursive function that assumes a flat list and switches when an index error occurs:

  def wtf(l, count=0):
    cl = l[0]
    try:
      return wtf(cl[count:], count+1)
      except IndexError:
          # Reverse the list and start over
          cl = l[::-1]
          return wtf(cl, 0)
Hopefully that gets my point across. Basically, exceptions are very useful, and there is nothing wrong with expecting and catching their throws under certain circumstances.
Re:I'm Sick of This by dasil003 on Friday April 10, 2009 @01:01PM (#556320)
Or I can just use my class and everything is a zillion times cleaner. I don't see why you're still arguing with me over this. My class is very clean and guaranteed to be compatible with all other code. Perhaps you got thrown by a previous commenter saying I "redefined nil?". But actually I did was define a nil? method on my internal UndefinedHash class, overriding the default Object implementation (ie. standard inheritance stuff, nothing dangerous).

I know you can do useful things with Exceptions when necessary. However I have to say unequivocably that using an exception in this case would be the worst possible idea. Why? Because the exception would be NoMethodError (ie. from nil[:key]). This equivalent to a NullException in Java, indicating all manner of generic logic errors that could have occurred anywhere within the stack.

If you looked at what I was actually doing, you might agree that SafeNestedHash is an incredibly useful abstraction. Hell, if it was written in PHP you could look at the algorithm and say, "wow, that's fast, elegant and readable." I'm not sure why you're so dead set that I'm doing something wrong, but you're just pulling this stuff out of thin air without any context.

Re:I'm Sick of This by jfarmer on Wednesday April 08, 2009 @06:40PM (#553544)
Because you want to be able to pass it to functions that expect Hash semantics?

Yay weak typing! cough

Re:I'm Sick of This by raganwald on Wednesday April 08, 2009 @01:10PM (#553003)
The difference between us is this: You see something that sickens you and publicly blame and shame others for the mess. I see something sickening and I am trying to clean it up.
Re:I'm Sick of This by tdavis on Wednesday April 08, 2009 @01:20PM (#553026)
I commend you for that, though I have no desire to clean up the messes of others. What would you have me do? Learn Ruby and start rewriting modules? Teach proper metaprogramming to the ignorant masses? The first step to fixing a problem, in this case, is conving people they have one. I'm throwing my hat into that ring.
Re:I'm Sick of This by raganwald on Wednesday April 08, 2009 @01:23PM (#553030)
Actually, I was just trying to pretend that I'm a kindly old wise man that everybody likes for his civic-minded attention to duty. In reality, I'm just as disgusted as you are and the post title says it all.

It's true that Ruby should provide better tools. But it's also true that a lot of what goes on is avoidable and there's nothing wrong with you pointing that out.

Re:I'm Sick of This by shiranaihito on Thursday April 09, 2009 @06:16AM (#554175)
a lot of what goes on is avoidable and there's nothing wrong with you pointing that out

Alright, so why the "(you) publicly blame and shame others for the mess" then?

Re:I'm Sick of This by plinkplonk on Wednesday April 08, 2009 @01:23PM (#553031)
"The difference between us is this: You see something that sickens you and publicly blame and shame others for the mess. I see something sickening and I am trying to clean it up."

I am not sure if I am reading this right, but there seems to be abit of a moralizing tone there. "The difference between us is you [ do something slightly slimy] , I [ do something very noble]".

At least that's the way it reads to me.

I am not sure in a discussion of a technical issue there is much of a difference in practice. Why is "this is sucky" not acceptable unless one has spent significant time trying to clean it up?

Edit : Retracted after reading "Actually, I was just trying to pretend that I'm a kindly old wise man that everybody likes for his civic-minded attention to duty. In reality, I'm just as disgusted as you are and the post title says it all.".

I thought I'll leave it up here as a warning not others not to read too much into something.

Re:I'm Sick of This by alaithea on Wednesday April 08, 2009 @10:30PM (#553805)
This is the main reason I've switched to Python in all my side-projects. I drank the Rails kool-aid for a long time, until I finally realized I couldn't get much done with it, as the libraries I wanted to use were unstable, and I couldn't understand the source, due to all the black magic going on. (Not to mention that you're forced to go to the source more often than not, because for some reason most Ruby projects' documentation sucks.) Sometimes I miss Ruby's expressiveness, but I'll take Python's abundant, stable and well-documented libraries any day.
Re:I'm Sick of This by petercooper on Wednesday April 08, 2009 @07:30PM (#553610)
It disgusts me on levels I cannot describe that this is standard operating procedure for ruby modules (gems, etc) and is the main reason I could never use it. Adding and replacing methods on builtin classes? Are these people out of their minds?! I don't understand it. I've used monkey patches once or twice and it always feels really dirty.

Which is why good Rubyists use these tools sparingly and sensibly. It is not "standard operating procedure."

Programmers of any language can misuse the tools made available. Tarring the language with the brush of its poorest users is disingenuous.