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
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 vooriedere 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 ;-)
Anton Astashov wo 19 mei 10 17:25
Cool trick! Clever and useful. Thanks!
Plaats je reactie