The Maybe Monad In Ruby

Just for fun, I decided to see what a monad would look like in ruby. I chose to use the Maybe monad since that’s what I used in my previous post on monads in C#. Here’s what I came up with:

   1: class Maybe

   2:   attr_accessor :value

   3:  

   4:   def initialize(value)

   5:     @value = value

   6:   end

   7:  

   8:   def get

   9:     if @value.nil?

  10:       nil.to_maybe

  11:     else

  12:       yield(@value).to_maybe unless @value.nil?

  13:     end

  14:   end

  15: end

  16:  

  17: class Object

  18:   def to_maybe

  19:     Maybe.new self

  20:   end

  21: end

  22:  

  23: class Foo

  24:   attr_accessor :bar

  25: end

  26:  

  27: class Bar

  28:   attr_accessor :baz

  29: end

  30:  

  31: foo1 = Foo.new

  32: foo1.bar = Bar.new

  33: foo1.bar.baz = "something"

  34:  

  35: foo2 = Foo.new

  36: foo2.bar = nil

  37:  

  38: result1 = foo1.to_maybe

  39:   .get{|f| f.bar}

  40:   .get{|b| b.baz}

  41:   .value || "nothing"

  42:  

  43: result2 = foo2.to_maybe

  44:   .get{|f| f.bar}

  45:   .get{|b| b.baz}

  46:   .value || "nothing"

  47:  

  48: puts "#{result1}" #=> something

  49: puts "#{result2}" #=> nothing

I find this code to be much easier to read and understand than the C# version even though the C# version makes a few of the monad formulae a little more apparent. For example, the binding function formula of (M t) –> (t –> M u) –> (M u) is fairly apparent when you look at a C# method signature like this:

   1: public Maybe<TResult> Get<TInput, TResult>(Maybe<TInput> maybe, Func<TInput, TResult> func) { ... }

In the ruby version, you don’t have the type declarations or the return value declaration, which gives us a little less of an immediate understanding that we are dealing with a monad. However, the tradeoff for this is that you get to keep your code very simple and you get to keep all of the binding functions for a given monad in the monads wrapper class. I’ve only implemented a get binding method, but you should be able to translate the other methods from the C# code fairly easily, using this example.

One thing I did not need to implement in the ruby version was a return method with a default value as a parameter. Remember in the C# version how we wanted to either return the value stored in the monad wrapper or some default value is the monad wrapper did not container one? Well, it turns out the || operator in ruby gives us this functionality without having to create a special to do that. On line 41 when I call .value || “nothing”, this is the equivalent of calling the .Return(someDefault) method in the C# version.

I suppose it’s not strictly necessary to add the .to_maybe method to the Object class, though. We could create a module to mix in to a specific object that we want, or we could just use the Maybe.new initializer… I put this method in there to make the final method chaining closer to the C# code that I started with. Perhaps that’s not such a wise idea, but I don’t know if it’s a bad idea or not… it works and it makes it easy to work with a maybe monad.

 

Handling Nil In Chained Calls: For @bennage

In the process of implementing and testing this, I remembered a conversation that I had with Christopher Bennage about a month and a half ago (no, my memory of the date isn’t that good… i have the IM logs, still :) ) where he was asking about a way to not throw exceptions when a nil is encountered in a chained call, in ruby. He wanted to write code like this:

   1: foo = bar.baz.widget.weeble.wobble

and have the code execute without error, even if baz or widget or any other part of the chain was nil. We discussed several options at the time, one of which involved a lot of nested blocks:

   1: def check(foo)

   2:   yield(foo) unless foo.nil?

   3: end

   4:  

   5: check(myvar){|x| check(x.y){|y| y.z }}

This works, but it’s a bit ugly to read and difficult to understand with all the nested blocks – especially when you get more than 2 or 3 items deep.

I also came up with a horrible, horrible way of using method_missing on the nil class to get it done… but it wasn’t a good solution. If you’d like to see what it was that I came up with, it’s available on this gist. Please, please, PLEASE do NOT use this code… it’s awful.

I think the maybe monad is what he was looking for, in the end. It’s not chained call syntax of “foo.bar.baz…”, but it gets the job done in a fairly elegant manner.

 

Is There A More Idiomatic Way To Do Monads In Ruby?

I haven’t actually done any searches for monads in ruby, yet. I wrote this code and this blog post “blind”, to see what I could come up with out of my own head. I’m wondering, though… is there a more idiomatic ruby way to do monads? I think I’ve incorporated a good number of ruby idioms already – especially the use of the || operator and the use of the implicit code blocks – but I’m thinking there may be more that I’m just not aware of.


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in Monads, Principles and Patterns, Ruby. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://wizardsofsmart.net/ Ryan Riley

    This cleared up a lot. You are not actually implementing Bind (SelectMany in LINQ), which is a very key ingredient to creating a monad. You are implementing Map (Select in LINQ). The signature for Get is the same as you would have Select:

    Maybe
    Select(this Maybe source, Func selector)

    Bind is different. To implement Bind, your Func should go from an input to a Monad
    , like so:

    Maybe SelectMany(this Maybe source, Func> selector)

    I went through this exercise previously with Ruby and found it very helpful. Here’s my maybe monad in Ruby: http://github.com/panesofglass/RubyOnMonads/blob/master/maybe.rb. I’ve got specs and more monad implementations in the rest of the project at http://github.com/panesofglass/RubyOnMonads/ and working on adding an implementation of Bart’s MinLinq.

    I hope that is helpful,
    Ryan

  • http://inger.myopenid.com/ Gergo

    Interesting article, thank you.

    > is there a more idiomatic ruby way

    Well am not 100% sure, but the “unless” in line 12 seems redundant to me.
    A perhaps more concise version of the get method:

    def get
          (@value.nil? ?  nil : yield(@value)).to_maybe 
    end

    # just in case nil and false should be treated the same
    def get   
    (@value &&  yield(@value)).to_maybe 
    end

  • https://github.com/ms-ati/rumonade Marc

    Hi Derrick, have you checked out the Rumonade gem, which implements a Monad mix-in for ruby, extends Array, and implements Option and Either classes? Please let me know what you think of it.

        https://github.com/ms-ati/rumonade

    Rumonade is modeled after the Scala standard library (with some influence from Scalaz and FunctionalJava), as once you’ve spent a few months working exclusively with Options and Eithers, it’s painful to go back to nil.