This is an old revision of the document!


Lab 11. Classes, Applicatives and More Monads

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.

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 = …

As you can see from :info, the type for our classes is unary (type Class :: * → Constraint), meaning that they expect to add just another type as parameter, as in MyList that expects and a.

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 ... 

Meaning “I already got type a, I expect you to give type b and apply your operations only on the object of type b.”.

Create objects for each class and test the validity of your class implementations with examples for show, fmap, <*>, »=.

Todo exemplu similar cu baza de date

1. \href{https://mmhaskell.com/monads/applicatives}{https://mmhaskell.com/monads/applicatives}

2. Dat din cartea de Haskell pentru Aplicative

3. Dat din cartea de Haskell pentru Monade