Applicative Functors

CIS 194 Lecture 10 & 11

To begin, consider the following:

type Name = Text

data Employee = Employee
  { name :: Name
  , phone :: Text
  } deriving Show
Employee :: Name -> String -> Employee

thus...

Motivation

Employee:: Maybe Name -> Maybe String -> Maybe Employee

Such data ? 

Employee :: Name -> String -> Employee

Much inputs ? 

Employee :: [Name] -> [String] -> [Employee]
Employee :: (e -> Name) -> (e -> String) -> (e -> Employee)

Wow ? 

Generalise ! (kapow)

wibbly :: (a -> b -> c) -> f a -> f b -> f c

It's a Unix system... I know this...

fmap :: (a -> b) -> f a -> f b

We have an extra wobbly though... so maybe...

fmap2 :: (a -> b -> c) -> f a -> f b -> f c
fmap2 f fa fb = undefined

Well, no.. not really.

h  :: a -> b -> c
fa :: f a
fb :: f b
h         :: a -> (b -> c)
fmap h    :: f a -> f (b -> c)
fmap h fa :: f (b -> c)
fmap :: (a -> b) -> f a -> f b

Dammit...

Applicative !!

finally, he gets to the point...

Formally:

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

There is only one "interesting" law for Applicative

f `fmap` x === pure f <*> x

Note that every Applicative is a Functor, by definition.

fmap2 redux

Recall:

h :: a -> b -> c
fa :: f a
fb :: f b

fmap h :: f a -> f (b -> c)

fmap h fa :: f (b -> c)
<*> :: Applicative f => f (a -> b) -> f a -> f b

New hotness

fmap :: Functor f => (a -> b) -> f a -> f b

Old and busted (not really)

Now

fmap2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
fmap2 h fa fb = (h `fmap` fa) <*> fb

Applicative was built by helpful types (ha!), so we have some extra goodies.

fmap2 = liftA2
(<$>) :: Functor f => (a -> b) -> f a -> f b
(<$>) = fmap

fmap has an infix version:

Thus

liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
liftA2 f a b = (f <$> a) <*> b

Can we build a bigger boat?

liftA3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
liftA3 h fa fb fc = h <$> fa <*> fb <*> fc

Operator precedence and associativity means we don't need piles of parentheses

What about 'pure' ?

While 'apply' (angle butt) handles the principle of 'contextual application'.

'pure' can be used to shift some function into some 'f'

pure :: a -> f a
<*> :: f (a -> b) -> f a -> f b
pure sum = ((Applicative f, Num a) => f ([a] -> a))
fangly :: (a -> b -> c) -> Maybe a -> Maybe b -> Maybe c
fangly c ma mb = pure c <*> ma <*> mb

Examples ! Woo!

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

Alternatively ... Point free stylezz

pure = Just
instance Applicative Maybe where
  pure = Just

  Nothing <*> ?? = ..
  ?? <*> Nothing = ..
  Just f <*> Just g = ..
instance Applicative Maybe where
  pure = Just

  Nothing <*> ?       = ..
  ?       <*> Nothing = ..
  Just f  <*> Just g  = ..
instance Applicative Maybe where
  pure = Just

  Nothing <*> _       = Nothing
  _       <*> Nothing = Nothing
  Just f  <*> Just g  = ..
instance Applicative Maybe where
  pure = Just

  Nothing <*> _       = Nothing
  _       <*> Nothing = Nothing
  Just f  <*> Just g  = Just (f g)

Woo! yay us!

Isn't that just functor? What use is having "yet another abstraction"?

The difference between the two, on the surface, is small and doesn't present itself as a great leap...

But Applicative represents a "model of computation" that Functor does not.

(<$>) :: (a -> b) -> f a -> f b

(<*>) :: f (a -> b) -> f a -> f b

As you already aware there are multiple levels of abstraction

Implementing your Applicative or Monad instances can be thought of as just one level. The "raw Haskell" level...

Once you have done that, you are able to move up a level.

Once you have the tiny moving parts...

You are able to combine them to create more advanced mechanisms.

Without needing to be (too) concerned with the underlying implementation.

You'll discover this in the homework !!

Oh yes... you have homework... 

A double batch in fact...

Quit whining...

._.

Applicative is awesome. THE END.

Yay it's over! Why do I keep agreeing to give presentations... ???? :(

Applicative Lecture 1:

http://www.seas.upenn.edu/~cis194/spring13/lectures/10-applicative.html

Applicative Lecture 2:

http://www.seas.upenn.edu/~cis194/spring13/lectures/11-applicative2.html

Homework #1 :

http://www.seas.upenn.edu/~cis194/spring13/extras/10-applicative/AParser.hs

Homework #2 :

http://www.seas.upenn.edu/~cis194/spring13/extras/11-applicative2/AParser.hs

http://www.seas.upenn.edu/~cis194/spring13/extras/11-applicative2/SExpr.hs

Do the home work in order, otherwise you'll be a sad panda...

data MyConf = MyConf
  { _remoteHost :: HostName
  , _remotePort :: Int
  , _setting1   :: SettingType
  , _setting2   :: OtherSetting
  }

-- our get thing from the loaded conf function.
getFromConf :: Config -> Text -> IO a

buildConfig :: FilePath -> IO MyConf
buildConfig fp = do
  rHost <- getFromConf c "remote.host"
  rPort <- getFromConf c "remote.port"
  set1  <- getFromConf c "setting1"
  set2  <- getFromConf c "other.setting2"
  return $ MyConf rHost rPort set1 set2
  where
    c = loadConfigFile fp

Using `do` notation, and basic constructor usage.

`MyConf` is lifted into context using `return`


buildConfig :: FilePath -> IO MyConf
buildConfig fp = MyConf
  <$> getFromConf c "remote.host"
  <*> getFromConf c "remote.port"
  <*> getFromConf c "setting1"
  <*> getFromConf c "other.setting2"
  where
    c = loadConfigFile fp

Using applicative...

lift `MyConf` into context with fmap (<$>)

Continue computation with apply (<*>)

Applicative Functors

By mankykitty

Applicative Functors

  • 1,755