Trivial Monad

Tags: haskell technical

Dan Piponi in his post about Monads started by taking a design pattern of a wrapper.

data W a = W a deriving Show

In this design pattern there are only two functions, one is to wrap anything with the wrapper. Let’s call it return' (otherwise it clashes with return in Prelude):

return' :: a -> W a
return' a = W a

The other function is to take an unwrapping-transforming function, take a wrapped value and to convert it into another wrapped value by using the function.

bind :: (a -> W a) -> W a -> W b
bind f (W a) = f a

So while we have the option of unwrapping the value using pattern matching:

g (W x) (W y) = ... -- do something with x and y

We would not want to do that because in real world, this wrapper is called Monad and it quickly becomes unwieldy to pattern match all of its subclasses. Any unwrapping of the values needs to be done in the bind function. Remember, we can access the unwrapped value in the function passed to bind as its first parameter.

This excellent explanation is followed by exercises.

Here are my attempts:

The first function takes an Integer and a Wrapped integer and creates their sum. The unwrapping happens in bind. This pattern is essentially about doing something with wrapped and free value.

-- g x (W y) => W (x + y)
g :: Int -> W Int -> W Int
g x wy = bind (\y -> W $ x + y) wy

The second function takes two wrapped values and create a new one. Here the =bind=s are nested and this does not look like a very clean approach.

-- h (W x) (W y) => W (x + y)
h :: W Int -> W Int -> W Int
h wx wy = bind (\y -> (bind (\x -> W $ x + y) wx) wy

Using the above definition of h, g can be redefined as:

g x wy = h (return x) wy

We also have the option of creating Functor, Applicative, and Monad instances for W:

instance Functor W where
  fmap f (W x) = W $ f x

instance Applicative W where
  pure x = W x
  W f <*> W x = W $ f x

instance Monad W where
  return x = W x
  W x >>= f = f x

So the code gets a little cleaner:

h' :: W Int -> W Int -> W Int
h' wx wy = wx >>= \x -> wy >>= \y -> return (x + y)

There’s also one method of unwrapping one layer of a doubly wrapped value:

join :: W (W a) -> W a
join wwa = wwa >>= id
-- join wwa = bind id wwa

This is a good example because the function which goes into bind has type of a -> W a, and id has the type a -> a. I could think about the second (commented) function only after coming up with the first one.

Therefore, >>= function unwraps one Monad value and transforms it to another Monad value.

W x >>= f = f x