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.
-- THIS COVERS FIRST PART OF THE VIDEO. -- 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.
Whats the point of these monads?
- Well, in my understanding, they are just a tool to make development easier.
- 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! :]