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?