So.

Is it as fun as it used to be?

A Little Unnecessary Smalltalk Envy

In A Little Head Trauma…: Returning None is Evil, Mark Derricutt provoked a brief moment of Smalltalk envy.


(self doSomethingThatMightReturnNil)
  ifNotNil: [val | val doSomethingWithNoHassle].

In addition to what Mark was actually responding to, it is an annoyance that I’ve had repeatedly in Ruby but never really considered fixing. Until now.

The annoying code:


tmp = doSomethingThatMightReturnNil
tmp.doSomethingWithNoHassle if tmp

Just look at those tmp variables.

This is the ‘cure’:


class Object
  def if_not_nil(&blk)
    yield(self) if blk
  end
end

class NilClass
  def if_not_nil(&blk)
  end
end

I like monkey patching.

And now I can write:


doSomethingThatMightReturnNil.if_not_nil { | v | v.doSomethingWithNoHassle }

Which is so much nicer [UPDATE: and nicer still after Sean prompted me to actually get Mark’s example right].

That really does look like Smalltalk, doesn’t it?

Advertisements

Written by hutch

November 22, 2007 at 11:58 pm

Posted in Ruby

13 Responses

Subscribe to comments with RSS.

  1. Not really.. because you’re passing the object to another function.. its easy enough to do a check inside doSomethingWithNoHassle on whether something is nil or not. Where Mark’s example highlights the beauty of the class heriarchy and yours does not (but could) is when you need to call a method ON the object.. so..

    doSomethingThatMightReturnNil.if_not_nil { | v | v.render }

    where calling v.render on nil would have caused a method not defined exception or whatever.

    Sean

    November 23, 2007 at 12:28 am

  2. You’re right, I should be paying more attention. I didn’t translate Mark’s example properly. I’ll fix that. Thanks!

    hutch

    November 23, 2007 at 12:35 am

  3. In Common Lisp:

    (when-let (val (do-somethitg-that-might-return-nil))
    (do-something-with-no-hassle val))

    ;)

    Pascal Costanza

    November 23, 2007 at 4:02 am

  4. And if you’re willing to tolerate the anaphoric Common Lisp stuff:

    
    
    (awhen (do-something-that-might-return-nil)
        (do-something-with-no-hassle it))
    
    

    which is completely untested by the way

    [Note to you non CLers: (do-something-with-no-hassle val) is dispatching on val, and so is more similar to ‘val.doSomethingWithNoHassle’ than ‘ doSomethingWithNoHassle(v)’]

    [Note to you CLers: I’m always a little jealous of CL :-) I generally program in two languages these days: Ruby, CL — but I miss Smalltalk, and APL for that matter]

    hutch

    November 23, 2007 at 9:02 am

  5. Hello Bob. I just wrote a couple of tests on http://blog.teksol.info/2007/11/23/a-little-smalltalk-in-ruby-if_nil-and-if_not_nil

    Shouldn’t that be a Gem ? Seems like a useful addition to Ruby. Maybe it could be added to Facets ?

    Thanks for sharing !

    Francois Beausoleil

    November 23, 2007 at 9:04 am

  6. Nice tests Francois. I’m going to have to start taking these little snippets seriously enough to write those tests myself.

    Now, the only thing left is to work out how to do the ‘else’ part. Smalltalk and CL both provide that capability. I can’t think of a satisfying way to do that in Ruby.

    hutch

    November 23, 2007 at 9:30 am

  7. This may not be even remotely elegant, but I did conjure this up for fun:

    class ElseHandler
    def otherwise(&blk)
    blk.call
    end
    end

    class Object
    def otherwise(&blk)
    self
    end

    def if_not_nil(&blk)
        if self.is_a? NilClass
            ElseHandler.new
        elsif blk
            yield self
        end
    end
    

    end

    Which should allow for:

    doSomethingThatMightReturnNil.if_not_nil { | v | v.render }.otherwise { panic! }

    Chris

    November 23, 2007 at 2:45 pm

  8. @Chris – You don’t even need to that with the original solution, assuming it returns self back. So you’d end up doing:

    doSomethingThatMightReturnNill
    .if_not_nil { | v | v.render )
    .if_nil { panic! }

    Mark Derricutt

    November 26, 2007 at 6:53 am

  9. Mark: that’s more or less what I was thinking of, but you loose the result which I think might be troublesome.

    Chris: That’s a good idea, but I don’t think you need the ElseHandler. What about:

    
    class Object
      def if_not_nil(&blk)
        yield(self) if blk
      end
      def if_nil(&blk)
        return nil
      end
      def otherwise(&blk)
        return self
      end
    end
    
    class NilClass
      def if_not_nil(&blk)
        return self
      end
      def if_nil(&blk)
        yield if blk
      end
      def otherwise(&blk)
        yield if blk
      end
    end
    

    This isn’t 100%, might need to do otherwise_if_nil and otherwise_if_not_nil…

    Francois: it seems that I’m still not doing unit tests. Sigh.

    hutch

    November 26, 2007 at 8:39 am

  10. Shouldn’t that be if_nil? and unless_nil?

    Brian Demant

    November 26, 2007 at 9:31 am

  11. Isn’t the ‘?’ usually used with predicates (i.e. methods that return true or false)? Maybe I’m confusing my languages here? :-)

    I understand where you’re coming from with the ‘unless’ suggestion, and it is kind of attractive, but maybe it’s a little too close to a double negative? It’d be nice to have a word that meant ‘not nil’. Kind of like if_something and if_nothing — not sure I like that either.

    hutch

    November 26, 2007 at 10:10 am

  12. http://pastie.caboo.se/122398 shows how to achieve following behavior:

    p 1.nonil + 2 + 10

    => 13

    p nil.nonil + 3 + 10

    => #

    No need for blocks if we can just swallow following messages.
    Might not be the best way, but yet another one :)

    manveru

    November 26, 2007 at 9:36 pm

  13. Aha – I thought of a similar (read: identical) way of doing it – http://howto.blog.bagofscum.com/2008/09/ifnotnil.html

    Asfand Yar Qazi

    October 23, 2008 at 10:59 am


Comments are closed.

%d bloggers like this: