why monads make lives easier

Demonstration of the composing nature of monads based on the great whiteboard presentation by the awesome Dr. Brian Beckman.

Scared of monads? Just not getting them?

Well..., you are not alone!

I have just stumbled over this awesome video demystifying monads by Dr. Brian Beckman which you can check out here.

After being done with it I felt the urge to revisit Haskell (which I sadly have left alone since my AoC2021) to just try out what he taught.

So here is a small example in Haskell for anyone that also greatly benefits/learns by coding the actual concept, rather than solely witnessing it.

-- Where we have functions of type a -> Ma
-- We created these functions with their corresponding types:
-- createListWithItem a -> Ma
-- addOneAndCreateListWithItem a -> Ma
-- doubleValueAndCreateListWithIt a -> Ma
-- doInsanceCalculation a -> Ma

-- This function has a -> Ma type from the video.
-- [] is a list in Haskell. Which is built as a Monad.
-- So our Monad type will be [] instead of M from the video.
createListWithItem :: a -> [a]
createListWithItem x = [x]

-- same as createListWithItem applies here.
addOneAndCreateListWithItem :: a -> [a]
addOneAndCreateListWithItem x = [x+1]

-- same as createListWithItem applies here.
doubleValueAndCreateListWithIt :: a -> [a]
doubleValueAndCreateListWithIt x = [x*2]

-- same as createListWithItem applies here.
doInsanceCalculation :: a -> [a]
doInsanceCalculation x = [(x + 1) * 3]

-- This function basicly has a -> Ma type from the video.
-- Here we see how we can puzzle our little building blocks together to realize
-- seemingly complex logic.
-- Ensuring the resulting type in compile time.
-- Composing as we need.
shoveIt :: a -> [a]
shoveIt a = createListWithItem a
            >>= addOneAndCreateListWithItem
            >>= doubleValueAndCreateListWithIt
            >>= doInsanceCalculation

-- The following is just synctactic sugar for the functon above. (Besides the if else)
-- That demonstrates that while every computation uses the previous value it
-- does not mutate it. Meaning in every step we take we have access to all
-- previous results. It is called "do notation".
shoveIt2 a = do
    x1 <- createListWithItem a
    x2 <- addOneAndCreateListWithItem x1
    x3 <- doInsanceCalculation x2
    if even x3 then return x3 else return x1

The source code can also be found here.

running the actual code snippet.
Testing the code out in the haskell REPL.

Whats the point of these monads?

  • Well, in my understanding, they are just a tool to make development easier. They:
    • make code consice/clear
    • allow the developer to use common syntax (e.g. shove/bind as seen above) for any data type that is a monad. No matter what the logic behind the specific monad really means.
    • give the developer a clear intuition about the code, by just seeing their data type. Some examples:

-- Without seeing ANY implementation detail, we already now:
-- This is a function with a computation that might fail. Thats it. The Maybe
-- monad.
fn1 :: a -> Maybe a

-- Again, without seeing ANY implementation details, we already now:
-- This is a function that uses side effects(IO) e.g. reads from std input and
-- might return an Integer. Thats it.
fn2 :: a -> IO Maybe Int

I hope you learned something today! See you soon! :]