Functors and applicative functors

Chapters from 9th to 11th in CIS 194 formed nice bundle of related lectures, so I tackled them as a group.

Functor is really just a type class used to define a type that can be mapped over. Just like an a list can be mapped over with map, functors can be too. Name of the function used in that case is fmap. Functor is defined as:

class Functor f where
  fmap :: (a -> b) -> f a -> f b
  (<>) :: a -> f b -> f a

Things one can fmap over include things like Maybe, List and Either among other things. For example, we can apply (map over) function on Maybe Integer, without having to check if the value is Just or Nothing:

> fmap (+1) $ Just 1
Just 2
> fmap (+1) Nothing

Applicative takes this idea one step further:

class Functor f => Applicative (f :: * -> *) where
  pure :: a -> f a
  (<*>) :: f (a -> b) -> f a -> f b
  (*>) :: f a -> f b -> f b
  (<*) :: f a -> f b -> f a

That’s a mouthful, but by keeping our heads cool, parseable and very useful. The idea is that with applicative, you can place a function in special context and apply values that are also in similar context to it. For example

> pure (+) <*> Just 2 <*> Just 4
Just 6
> pure (+) <*> Nothing <*> Just 4
> (++) <$> Just "Hello " <*> Just "World"
Just "Hello World"

Different applicatives behave differently. Maybe act as a computation that might fail, while List is computation with uncertain answer:

> (+) <$> [1, 2] <*> [4, 5]

Exercise in applicatives was about building parsers. The idea was to use Parser defined as:

newtype Parser a = Parser { runParser :: String -> Maybe (a, String) }

and build small parsers that could then be combined to bigger parsers. One could even have simple logic like a or b while choosing which parser to apply next. For example, to define a parser that takes a string of two integers and pairs them up into a tupple, one could use following:

intPair :: Parser String
intPair = pairUp <$> posInt <* space <*> posInt
          where pairUp a b = show a ++ " " ++ show b
                space = satisfy (== ' ')

and in action. Notice how unparsed text is also returned. This allows chaining of parsers together. First one parser eats string a little bit a returns result and rest of the string, then next parser does the same thing and process continues:

> runParser intPair "23 34 56 foo"
Just ("23 34"," 56 foo")

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s