the anarchy of ruby

Posted by crayz
at 02:26PM on 03/01/2008

It seems the zeitgeist in ruby-land is looking for a solution to the ‘do this if I can’ patterns that continually crop up in code. In the python community you’d wait for the problem to be solved by Guido the benevolent dictator. With PHP there’d be three near-identical functions added to the global namespace in a minor point release. With Java you’d wait a couple years for a JSR, a couple more for the next release of the language, and a couple more for enterprisey vendor support. In the meantime you’d copy & paste around a “design pattern” solution to make yourself feel better

In Ruby, a bunch of people just go out and write their own, and then everyone else votes with their code:

Each solution has its own set of trade offs. For my money, #andand is the clear winner. It’s got the cleanest syntax(no symbols) and is closely tailored to the case of wanting to call a method if the receiver isn’t nil. #try is interesting, and might be worth rewriting to work the way #andand does. It gives you the same benefits of calling methods on possible nils, although it would behave in a way that might seem unexpected for methods that nil actually has, since doesn’t undef those the way #andand does. However #try also stops you from sending any message the receiver can’t respond to, which strikes me as overkill for most situations

And then #do_or_do_not, which just sends the damn message and rescues any exceptions. This I think is a huge mistake. When I first started coding ruby I abused the inline rescue exactly this way, just haphazardly rescuing any lines of code that might fail. It quickly became a nightmare for maintenance and bug hunting, because you wind up unintentionally swallowing ‘real’ exceptions when all you meant to do was stop the code from blowing up calling a method on nil. #do_or_do_not will be a little safer because the swallowed exceptions will be limited to the method called rather than an entire line of code. Still, this seems far more dangerous than it’s worth. Frequently throwing exceptions also causing performance issues when you’re neck-deep in the stack (just ask Twitter)

Comments

Leave a response

  1. anonymousMarch 03, 2008 @ 03:08 PM

    I’m a fan of ergo() from facets:

    http://facets.rubyforge.org/quick/rdoc/core/classes/Kernel.html#M000357

  2. EricMarch 03, 2008 @ 03:34 PM

    In the Python world they actually have the concept of Python Enhancement Proposal that is used to spec and implement any addition to the language or standard library. The process is relatively simple and forces people to put their money where their code is in terms of an implementation. The result is that even if a PEP is not accepted (often times because the use case or need is not large enough within the community), the authors can still use the result and see if their ideas really stand up in practice. The result is an amazingly stable standard lib and excellent additions to Python.

    Personally, I would love to see a similar pattern be adopted by the Ruby community as it could help to bring about not only better features, but establish best practices. I think this would help improve Ruby as a language and most importantly reveal the elegance in its design.

  3. Sam Livingston-GrayMarch 03, 2008 @ 03:59 PM

    See also Turtles! and The Maybe Monad In Ruby, both of which address the issue of method chaining when any intermittent method might have returned a nil.

  4. GuillermoMarch 03, 2008 @ 05:08 PM

    Eric, Ruby has RCRs. Check http://rcrchive.net

    I don’t know why the RCRs aren’t as known as PEPs, but ruby does have a way (besides mailing list).

  5. Reg Braithwaite March 03, 2008 @ 05:51 PM

    ergo from Ruby Facets has identical behaviour to Object#andand. Actually, #ergo was written first and #andand was developed in ignorance of #ergo, so it’s fair to say that #andand is an unwitting (or witless) clone of @ergo.

    But I still like the #andand name… Mostly because of the way it reads over the telephone:

    foo &&= Foo.find(:first, ...) # “foo and and equals…” foo.andand.bar(42) # “foo and and dot bar…”

    :-)

  6. i386March 03, 2008 @ 09:54 PM

    Ruby is the new Perl.

    Enjoy your slow VM and abhorrent assortment of language “features”.

  7. sheMarch 04, 2008 @ 02:05 AM

    “Ruby is the new Perl. Enjoy your slow VM and abhorrent assortment of language “features”.”

    You troll.

    Perl is ugly. Ruby is beautiful.

    The “slowness” is no valid argument. Perl was slower than C but it was successful. Think about it. “Slowness” is simply no argument even more so as computers become faster and faster.

    It seems to me that either certain people exaggerate, or there are simply trolls. If something is too slow for you, go away and stick to C for eternity.

  8. Brian AdkinsMarch 04, 2008 @ 01:31 PM

    The andand implementation isn’t that bad, I suppose, but I think it’s simpler to just solve this with a function. I don’t see what tags you allow and there’s no preview button, so I’ll give pre a shot:

      # iif is short for invoke_if
      # e.g. name = iif(get_an_Object(...), :my_method, a, b)
      def iif obj, method, *args
        obj && obj.send(method.to_sym, *args)
      end
    
  9. Reg BraithwaiteMarch 04, 2008 @ 03:59 PM

    Brian:

    I like iif, thanks! of course, Ruby doesn’t have functions. So… if you want to use it throughout a project, you will have to open up Object and embed it there. That way it will have the appearance of a function but it will really be a method.

    That’s the approach with “functions” like returning and with.

  10. Erkki LindpereMarch 04, 2008 @ 04:33 PM

    “With Java you’d wait a couple years for a JSR, a couple more for the next release of the language, and a couple more for enterprisey vendor support. “

    If you mean that this is the general way how reuse/sharing happens in Java then you are dead wrong, but in this specific case (and many other cases) it’s true that this doesn’t have an easy solution in Java without modifying the language.

    But Scala makes this easy to do by default, while retaining the static types. As long as you use Scala’s idiom of pretending that null doesn’t exist and using Option[T] for optional things, which has subtypes Some[T] and None:

    var meaningOfLife: Option[Int] = Some(42) meaningOfLife map (_.toString) // yields Option[String] = Some(“42”) meaningOfLife = None meaningOfLife map (_.toString) // yields None without evaluating the closure

  11. x128March 04, 2008 @ 05:23 PM

    She, Ruby appreciates you sticking up for her/him/it.

  12. TransMarch 05, 2008 @ 01:18 AM

    Hi—

    Though I added a bit of extra functionality to #ergo, I want to make sure everyone knows it was Daniel DeLorme who created it—AFAIK.

    T.

  13. Shane.March 05, 2008 @ 08:36 PM

    “You troll. Perl is ugly. Ruby is beautiful.” Pot, meet kettle. The skill of the artist determines beautiful code, not the language.

  14. SpacebatMarch 06, 2008 @ 06:10 AM

    “You troll. Perl is ugly. Ruby is beautiful.”

    Eye of the beholder.

    !/usr/bin/ruby

    def inspect “I just broke introspection for every object in the interpreter” end

    !/usr/bin/perl

    sub push { “push() is overridden only in this package and you can call CORE::push()” }

  15. DARMarch 20, 2008 @ 12:22 PM

    Actually, in Java, there would quickly be a half-dozen or more open source libraries written to provide the needed capability, one of which would then quickly become the de facto standard that most Java developers would use from then on. A JSR, if it ever came, would be pretty much irrelevant to the average developer.

    Man, the cluelessness of most Java-bashers as to how things really work in the Java world (as opposed to the “big enterprisey” prejudice of how they think it works) never ceases to amaze me!