DEV Community

Noah Hradek
Noah Hradek

Posted on

Monads for Great Fun

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

In this do form.

doSomething = do
    Just 2
    Just 3
    Nothing
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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}"
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Top comments (0)