Parsing A Hash Tree (Or Object Graph) Using The Maybe Monad In Ruby

I was playing around with the maybe monad that I wrote in my last post, and it occurred to me that I might be able to parse out a object graph and grab a value from that graph with the monad. For example, look at this hash tree:

   1: stuff = { 

   2:   :foo => {

   3:     :bar => nil

   4:   },

   5:   :bar => {

   6:     :baz => :whatever,

   7:     :what => {:ever => nil }

   8:   }

   9: }

There’s nothing terribly special about this tree… it’s just a bunch of name / value pairs… leafs and branches. It’s a typical tree structure.

What is interesting – to me at least – is the ugly code that I usually write to parse through a tree like this. It always involves a number of if-then statements that are nested inside of each other in order find the specific value or values that I’m looking for. What I wanted to do, instead, was see if I could use the maybe monad in a nested, structured manner to parse the tree and find the values that I want. The interesting thing is that the monad can only return one value… so I had to decide on how to approach this. I decided to return the first value I ran into. In this case, the :bar/:baz node has a value of :whatever, so that’s what it should return.

Using the code blocks from the monad’s get method, I can nest additional maybe monads:

   1: stuff.to_maybe.get{|s| s[:foo].to_maybe.get{|f| f.bar }.value }

Note that i have to return the .value of the nested monad, or it will end up wrapping a maybe type inside of a maybe type.

Then, by using some well place || or operators, I can branch the parsing of the tree and either return the first value if one was found, or return the next value.

   1: stuff.to_maybe.get{|s|

   2:   s[:foo].to_maybe.get{|f| f[:bar]}.value} ||

   3:   s[:bar].to_maybe.get{|b| b[:baz]}.value}

   4: }

This will either return the :foo/:bar value if a value is found, or it will return the :bar/:baz value if one is found.

The final parsing structure to check all of the possible leaves for values, and return the first one that is found to have a value, looks like this:

   1: result = stuff.to_maybe.get{ |s| 

   2:   s[:foo].to_maybe.get{ |f| f[:bar] }.value ||

   3:   s[:bar].to_maybe.get{ |b| 

   4:     b[:baz].to_maybe.get{ |z| z}.value ||

   5:     b[:what].to_maybe.get{ |w| w[:ever]}.value

   6:   }.value

   7: }.value || "nothing"

Of course, this isn’t limited to hash trees. You can easily parse object graphs in the same way. I just picked hash in this case because it’s a situation I’ve run into a lot. And honestly, this code is still a little difficult to read… it definitely needs some cleanup work. Overall, I think it’s a step in the right direction on improving the horrible if-then code that I’ve been writing around these types of tree structures, but I’m sure it can be done better.

So, what can I do to make this code even cleaner and easier to understand? Or am I barking up the wrong tree on this, and need to find a different approach for a cleaner solution?


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, Ruby. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://joshuaflanagan.lostechies.com Joshua Flanagan

    Not sure I completely follow the intention (‘monads’ lose me), but I wonder if you could follow a deep path in a hash using something like this (if this isn’t already built in somewhere):

    http://gist.github.com/620536

  • http://www.lostechies.com/members/derick.bailey/default.aspx derick.bailey

    josh – yeah, that would work specifically for the hash tree scenario that i used in this example. the maybe monad works for any given object graph, though. i just happened to use a hash tree here.

    the intention is to avoid having a lot of nested if-then statements to traverse the tree or object graph. i want a more object-oriented approach…something well structured, not procedural.

    i’m not sure that this is any better, yet. it’s still pretty ugly. hoping to find some ways to clean this up.

  • http://joshuaflanagan.lostechies.com Joshua Flanagan

    Here is one that would allow you to call a chain of method on an object graph, safely returning nil, if any encountered along the way:

    http://gist.github.com/621602

  • http://wizardsofsmart.net/ Ryan Riley

    I just added a spec describing divide by 0 errors using the maybe monad here: http://github.com/panesofglass/RubyOnMonads/blob/master/specs/maybe_specs.rb

    I have not seen the use of a maybe for traversing a tree before; you’d probably have an easier go of it using a leaf/branch recursive loop.

    Ryan