Monad tutorials have achieved what many startups crave – hockey stick growth.
This isn’t a monad tutorial.
It could be a monad tutorial, but I still don’t understand monads. I don’t think I ever will.
Monads were invented to fuck with us developers of lesser minds, the ones who don’t want to bend over backwards to maintain functional purity and would perhaps rather … you know … get things done instead. Really, sometimes I just want things to work. I don’t even care how.
You see, monads are just a wrapper around values. Their rhetoric is Hey, there’s this value here, but there’s a whole bunch of conditions involved. You don’t really need to know about those, just play according to the rules.
And then you’re like Okay … but just give me the value? I understand all the conditions, but I don’t care about them, I just want the value.
Well no, no you can’t have the value just like that. That would be silly. You can unwrap the monad, play around with the value, fiddle with all the knobs and dials, but once you’re done, you have to put it back into the monad where it’s safe.
Monads are the ultimate Dad’s shiny red corvette that he lets you drive around the parking lot, but you must always put it back in the garage and never drive anywhere with it.
I wish my dad had a corvette.
Monads annoy me
Really, they’re just annoying. Annoying little shits.
Yesterday I was doing something simple:
- take some values from mongodb
- draw a graph
I spent the entire day in Monad Hell getting it done in Haskell. And that’s after the dependency hell of installing Chart on my computer … cabal isn’t very good, but that’s a whole different blogpost.
You see, when you take some values out of Mongo, the operation might have failed! You know it didn’t, you know everything should just crash and burn and alarms should be going off if it did, so it’s never going to. Not as far as this piece of code is concerned.
But Haskell doesn’t know that. It might have failed. So everything you get out is wrapped in the Maybe monad. Except sometimes it isn’t.
known'::[Document] -> (UTCTime, Double) known' docs = (cast' $ (valueAt "time") (docs!!0), cast' $ (valueAt "amount") (docs!!0)) -- Couldn't match expected type `Double' with actual type `Maybe a0' -- In the expression: cast' $ (valueAt "amount") (docs !! 0)
Great. Ok so those are maybes, don’t really care, moving on.
By the way, if the function really does fail it either dies with an index out of bounds error (not enough Documents) or with some sort of fields weren’t there error, if you give it the wrong sort of Document. So it’s not like it could actually fail in a way that it would still work and those Maybes would be warranted.
plot.hs:52:7: Couldn't match expected type `Double' with actual type `Maybe Double' In the first argument of `(+)', namely `money' In the first argument of `(-)', namely `money + p' In the expression: money + p -
At this point you’re (or at least I was) getting frustrated and you suddenly can’t remember where that short and sweet description of instance was even though it kept slapping you in the face the whole time you were trying to figure out how to take a value out of some monad or another and keep it the fuck out.
I don’t know what instances are, not completely, but they look like a magic thing that can make type conversions automatic. And that’s awesome so I need to look it up.
Anyway, you settle for a simple function
double'::Maybe Double -> Double double' Nothing = 0.0 double' (Just x) = x
Everything works! Yay!
At no point in execution does that default value actually happen. Because you’ve made sure externally that the operation Haskell is unsure of will never fail, but hey, you’re happy, haskell is happy, the code is somewhat ugly.
But at least it works.
Now I just wish the graph wasn’t a fluke of currency conversions.