This is an old revision of the document!
Lab 11. Classes, Applicatives and More Monads
11.0 Introduction
A powerful tool for understanding classes in Haskell is the :info <class>
in the ghci command line:
>:info Eq type Eq :: * -> Constraint -- We'll return to this a bit later :-) class Eq a where -- Shows the blue-print of the class (==) :: a -> a -> Bool (/=) :: a -> a -> Bool {-# MINIMAL (==) | (/=) #-} -- The minimal requirements to be implemented by a data type -- to be included in the class -- Defined in ‘GHC.Classes’ instance (Eq a, Eq b) => Eq (Either a b) -- Instances of the class -- Defined in ‘Data.Either’ instance Eq a => Eq (Maybe a) -- Defined in ‘GHC.Maybe’ instance Eq Integer -- Defined in ‘GHC.Num.Integer’
If we look at Monad:
>:info Monad type Monad :: (* -> *) -> Constraint class Applicative m => Monad m where (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b return :: a -> m a {-# MINIMAL (>>=) #-} ...
We see that the only requirement for Monad to work is overriding ( »= )
.
However, the catch comes from the restriction
class Applicative m => Monad m ...
By looking at the Applicative:
>:info Applicative type Applicative :: (* -> *) -> Constraint class Functor f => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b GHC.Base.liftA2 :: (a -> b -> c) -> f a -> f b -> f c (*>) :: f a -> f b -> f b (<*) :: f a -> f b -> f a {-# MINIMAL pure, ((<*>) | liftA2) #-}
We see that besides $ ( >>= ) $, we need pure and $ ( <*> ) $.
If we recall the definitions from the previous laboratory:
instance Applicative Foo where pure = return ff <*> fx = ff >>= (\f -> fx >>= (\x -> return (f x)))
We see that pure and (<*>) are implemented using return and ( »= ), the actual needs to implement a Monad.
However, it might initially be curious how we implement the functions of a class deemed mandatory for another with calls from the latter. This is due to the ad-hoc polymorphism in Haskell and the language's recursive nature (recall the linked definitions between $(==)$ and $(/=)$). And historically, Monad was added before Applicative in Haskell, so this assures backwards compatibility.
However, as you will see in the next exercises, you can first implement the instance for Applicative and use its functions to derive Monad.
11.1. Tasks
For the following exercises: Define the data structure and instances for Eq, Show, Functor, Applicative (in 2 ways from scratch and based on Monad) and Monad (in 2 ways from scratch and based on Applicative) for the following:
1. data MyList a = …
2. data MyTree a = …
3. data MyMaybe a = …
4. data MyPair a b = …
However, MyPair needs 2 types to form an object. We can think of MyPair as a hash set (key, value) and curry the types:
instance Class (MyPair a) where ...
thus saying “I already got type a, you need to apply your operations only on type b.
Create objects for each class and test the validity of your class implementations with examples for show, fmap, <*>, »=.