Monads for Great Fun
Noah Hradek
Posted on March 4, 2023
One of my favorite books on Haskell is Learn You a Haskell by Miran Lipovača. Often monads are interpreted in a mathematical or purely functional context, and this is the approach in Learn You a Haskell. However, their reach goes far beyond that. In this tutorial, I won't entirely explain what monads are; I assume you already have a decent knowledge of this. If not, read the monad section of the book or read another tutorial like on the Haskell site.
Instead, I will show you what monadic computation is capable of in terms of various interesting applications. To briefly review, a monad is a computational context with an operator bind (>>=) which takes in a monadic value then applies a function to the unwrapped value and returns another monad. It's like a wrapper that wraps another value with context. Looking at the type signature you can see what I mean.
(>>=) :: m a -> (a -> m b) -> m b
Another operator (>>) does the same thing but without any input. This operator merely chains together two monads without any function applied.
(>>) :: m a -> m b -> m b
The do syntax just does (>>) repeatedly without needing to write the operator explicitly. For example we could write the following code.
doSomething = Just 2 >> Just 3 >> Nothing
In this do form.
doSomething = do
Just 2
Just 3
Nothing
We can use the (<-) operator to unwrap the monadic value and return a normal unwrapped value. For example, getting a line of text from an IO monad and printing the value.
main = do
ln <- getLine
putStrLn ln
But there are some there example where Monads might be very useful. I'll go over a few of these.
HTTP
One example would be for HTTP authentication passing. I'm not familiar if existing frameworks like Yesod implement this or not. However, we could do something like this to store an authentication context instead of having to pass it each time as a header.
getFromWeb :: HTTP ()
getFromWeb = do
authentication "Bearer: TOKEN"
res1 <- get "http://someurl.com/api/get"
post "http://someurl.com/api/post" "{JSON data}"
The authentication function would return a HTTP monad with the authentication context of a bearer token.
Graphics
Often times drawing is implemented as a complex state machine with many parameters to keep track of. For example in OpenGL you have to set the viewport then the vertex arrays and so on. In Processing and Javascript with the canvas you have to do something similar like this.
const ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(50, 140);
ctx.lineTo(150, 60);
ctx.lineTo(250, 140);
ctx.closePath();
ctx.stroke();
This is the perfect example to "monadize." We could write something similar using a path monad like this.
stroke :: Context2D -> Path -> DrawIO
createShape :: Path
createShape = do
moveTo 50 140
lineTo 150 60
lineTo 250 140
main :: DrawIO
main = do
stroke $ (createContext "2d") createShape
No more needing to keep track of where the path begins and ends and we no longer need to keep track of the drawing context.
Sound
I was thinking of applying monads to signal processing for audio. Let's say we have a signal monad which carries context about the signal. Imagine we took in an input signal and passed it through a low-pass-filter at 440hz and then a delay line of 0.5ms, it might look something like this with binds.
filterAndDelay :: Signal -> Signal
filterAndDelay input =
input >>= lpf 440 >>= delay 0.5
Posted on March 4, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.