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: }

</div> </div>

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 }

</div> </div>

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: }

</div> </div>

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"

</div> </div>

</p>

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?

The Maybe Monad In Ruby