Itsy bitsy monad

Jeroen Bulters wo 31 mrt 10

Functional programming has been sort of a hype lately. Therefore I thought it would be nice to try and apply some concepts from functional programming languages to Ruby (and, ergo: Rails).

One of my personal best friends in Ruby is Mr. NilClass (not really), he always makes me add stuff to my if statements to check if he’s there. So, I’m constantly doing stuff like:

if (customer && customer.address && customer.address.zipcode)

Sure, not to big of a deal, but I just don’t like it.

Several functional programming languages know a concept known as Monad’s. I’m not going to give an introduction to Monad’s (read a good one at moonbase), but I can give a useable monad implementation in Ruby to illustrate the concept.

Monad’s basically wrap an object in a container, allow you to perform actions on it and – when you’re done playing – unwrap the resultant value from the monad.

So, after constructing the monad, and connecting some of the functionality to the Ruby Object class. I should be able to do:

any_object.if_possible?.other.value #=> 'the value I put in here'
nil.if_possible?.other.value #=> nil
any_object.if_possible?.nil.value #=> nil

All without yielding any exceptions.

Let’s start with adding the if_possible? method to wrap our object in a monad.

class Object
  def if_possible?
    Monad::Maybe.new(self)
  end
end

Note: This monad is actually called the Maybe monad in a.o. Haskell

Now, we create the monad itself (nicely in the Monad module). Please note that the mentioned unwrapping is achieved through the attr_accessor and that you should choose a more semantic name for it.

module Monad
  class Maybe
    # Unwrapping through the accessor
    attr_accessor :value
  
    def initialize val
      @value = val
    end
    
    def monadBind proc
      if @value != nil
        # We get the result, and rewrap
        # that in a the Maybe monad again.
        proc.call.if_possible?
      else
        self
      end
    end

    def method_missing(method_name, *args)
      monadBind(Proc.new {@value.send(method_name, *args)})
    end
  end
end

Now let’s try this out with the following file:

class Test1
    attr_accessor :other
    def initialize(other)
        @other = other
    end
end
class Test2
    attr_accessor :value
    def initialize(value)
        @value = value
    end
end

Load it into irb (or put the following in the same file) and try this:

value = Test2.new(true)
working = Test1.new(value)
puts working.other.value
puts working.if_possible?.other.value.value
puts nil.if_possible?.other.value

When run, it produces the following output, without throwing exceptions:

[bulters@~/monadic_art]$ ruby monad.rb 
true
true
nil

So, what did we do? Basically, we constructed a nicer way to check for nil’s, and propagating the handling of it back to our program.

The line

if (customer && customer.address && customer.address.zipcode)

can now be written as:

if(customer.if_possible?.address.zipcode.value)

which is – in my opinion – not only shorter, but also a bit easier on the eyes.

Gepost in hor |  1 reactie

Anton Astashov wo 19 mei 10 17:25

Cool trick! Clever and useful. Thanks!

Plaats je reactie





Welkom op Holland On Rails

Het startpunt voor Ruby On Rails in Nederland. Vind de laatste technieken, meningen en nieuwtjes.

Recente Jobs

Gezocht: Ruby On Rails ontwikkelaar (junior of senior)

Eet, drink en droom jij over Ruby On Rails? Wil jij het liefste dag en nacht bezig zijn met jehobby; super coole webapplicaties ontwikkelen in Ruby On Rails?

Dan willen wij jou graag een podium bieden om je Ruby skills te vertonen aan onze nationale en internationale klanten!

@ Internetbureau Holder, Obdam

Bekijk alle jobs »»

Gereedschapskist

Onmisbare tools voor
iedere developer!
Ruby On Rails
Framework voor de web 2.0 developer. Eindelijk vooruitgang!
TextMate
Editor for true pro's
Typ, tab, top :-)
Nee, niet voor Win.
Made On A Mac
En nou is het over met die saaie grijze Windows bak van je!

Auteurs op deze site

Chris Obdam

'Less is more' evangelist, past dit ook dagelijks toe op zijn tandenborstel.

Chiel Wester

Snelheidswonder op Ruby wielen. Leuk om mee te pair-programmen ;-) Recommend Me
Src-120-attending

Stephan Kaag

Het eerste Rails coreteam- member uit Nederland? Rails evangelist van het eerste uur.

Paul Engel

Én Rails programmeren én interfaces designen? Je zou hem superman kunnen noemen..

Dax Huiberts

Official Zip-Programmer, skinny code is helemaal zijn ding. Haalt meer code weg dan dat er bij komt.

Freek Monteban

Het nieuwste telg uit het Holland on Rails nest! Hij doet niets anders meer!

Johan Vermeulen

De stylesheet-koning uit de kop van Noord-Holland!