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