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
it clashes with
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
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
-> 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.
>>= function unwraps one
Monad value and transforms it to
W x >>= f = f x