Applicative style programming
jlgarhdez HaskellMADpepegar
Pepe García
Scala Engineer @ Fidesmo
Co-organizer @ HaskellMad & FP-MAD
Scala fan
Haskell Acolyth
Functional Programming Devotee
jlgarhdez HaskellMADpepegar
About this talkSlides are available here:
http://es.slideshare.net/JosLuisGarcaHernndez/applicative-style-programming
Code is available here:https://github.com/pepegar/Control.Applicative.Talk
jlgarhdez HaskellMADpepegar
What are we learning today?
● What are applicatives?● When do we use them?● Who is using them?● How to use them?
jlgarhdez HaskellMADpepegar
What are applicatives?
Applicatives are an abstraction between functions and monads. And they open the doors for the so called applicative style!
jlgarhdez HaskellMADpepegar
What are applicatives?
@jlgarhdez
class Functor f => Applicative f where
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
jlgarhdez HaskellMADpepegar
What are applicatives?
Let’s understand the typeclass:
It exposes a `pure` function, that puts a value inside the applicative context
And a `<*>` function, that applies a function inside an applicative context to the content of another value with the same applicative context
jlgarhdez HaskellMADpepegar
What are applicatives?
Do you remember fmap from functors? Try to compare it with <*>
fmap :: (a -> b) -> f a -> f b
(<*>) :: f (a -> b) -> f a -> f b
jlgarhdez HaskellMADpepegar
What are applicatives?Basically an Applicative is a Functor that can also apply a function contained in a container to other container of the same type.
Prelude Control.Monad> let fn1 = \ x-> x * x
Prelude Control.Monad> let fn2 = \ x -> x + 33
Prelude Control.Monad> let fns = [fn1, fn2]
Prelude Control.Monad> :t fns
fns :: Num a => [a -> a]
Prelude Control.Monad> let nums = [1,2,3,4]
Prelude Control.Monad> :t nums
nums :: Num t => [t]
Prelude Control.Monad> fns <*> nums
jlgarhdez HaskellMADpepegar
A small example
As an example of Applicative Programming, let’s see how Applicative and Monadic styles compare:
jlgarhdez HaskellMADpepegar
A small exampledata Person = Person {
name :: String,
lastName :: String
} deriving Show
data Err = String
validateName :: String -> Either Err String
validateName n = if n == "Pepe" then Right n
else Left "name is not Pepe"
validateLastName :: String -> Either Err String
validateLastName l = if l == "García" then Right l
else Left "last name is not García"
jlgarhdez HaskellMADpepegar
A small example. Monadic
validatePersonM :: String -> String -> Either String Person
validatePersonM n l = do
vName <- validateName n
vLast <- validateLastName l
return $ Person vName vLast
jlgarhdez HaskellMADpepegar
A small example. Applicative
validatePersonA :: String -> String -> Either String Person
validatePersonA n l = Person <$> validateName n <*> validateLastName l
jlgarhdez HaskellMADpepegar
A small example. Applicative (2)Why does the previous example work? Let’s dig a bit deeper on it:
Prelude> :t Person
Person :: String -> String -> Person
Prelude> :t Person <$> validateName "pepe"
Person <$> validateName "pepe" :: Either String (String -> Person)
Prelude> :t Person <$> validateName "pepe" <*> validateLastName "Garcia"
Person <$> validateName "pepe" <*> validateLastName "Garcia" :: Either String Person
jlgarhdez HaskellMADpepegar
A small example. RecapSo, what do we learn from our small example?
Monads are not a silver bullet, and can be replaced easily by Applicative Functors when ordering is not important.
jlgarhdez HaskellMADpepegar
When to use applicatives?
When we need more than a functor, and less than a monad…
Sounds appealing, but WTF is this!?
jlgarhdez HaskellMADpepegar
When to use applicatives? Functor
Functor defines containers that can be mapped over.
class Functor f where
fmap :: (a -> b) -> f a -> f b
jlgarhdez HaskellMADpepegar
When to use applicatives? Monad
Monad defines containers allow operation sequencing!
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
return :: a -> m a
fail :: String -> m a
jlgarhdez HaskellMADpepegar
When to use applicatives?Some rule-o-thumbs I use for detecting Monad overkill
● import Control.Applicative● replace return with pure, liftM with (<$>) (or fmap or liftA), liftM2 with liftA2,
etc, and ap with (<*>)● If your function signature was Monad m => ..., change to Applicative m => ...
(and maybe rename m to f or whatever).
jlgarhdez HaskellMADpepegar
When your do block can be substituted by liftM2/3/4...
When to use applicatives?
validatePersonM :: String -> String -> Either String Person
validatePersonM n l = do
vName <- validateName n
vLast <- validateLastName l
return $ Person vName vLast
becomes
validatePersonA :: String -> String -> Either String Person
validatePersonA n l = liftM2 Person $ validateName n $ validateLastName l
jlgarhdez HaskellMADpepegar
When your operations does not depend on each other
When to use applicatives?
validatePersonM :: String -> String -> Either String Person
validatePersonM n l = do
vName <- validateName n
vLast <- validateLastName l
return $ Person vName vLast
becomes
validatePersonA :: String -> String -> Either String Person
validatePersonA n l = Person <$> validateName n <*> validateLastName l
jlgarhdez HaskellMADpepegar
Who uses Applicative?Applicative is being used all over the place nowadays. Some interesting examples of its use are:
● HAXL. Efficient data access● CmdTheLine. Command line argument parsing● Validation. Data validation library● Attoparsec. ByteString parsing library
jlgarhdez HaskellMADpepegar
How to use Applicatives?
@jlgarhdez
Applicatives are really easy to use. You just need to provide an instance of the typeclass for your data type.
data Maybe a = Just a
| Nothing
instance Applicative Maybe where
pure = Just
Just f <*> m = fmap f m
Nothing <*> _m = Nothing
jlgarhdez HaskellMADpepegar
Or, you can go Free!
jlgarhdez HaskellMADpepegar
Free Applicatives
Create an applicative from any Functor
jlgarhdez HaskellMADpepegar
Free Applicatives.
data Ap f a where
Pure :: a -> Ap f a
Ap :: f a -> Ap f (a -> b) -> Ap f b
@jlgarhdezjlgarhdez HaskellMADpepegar
Free Applicatives.instance Functor (Ap f) where
fmap f (Pure a) = Pure (f a)
fmap f (Ap x y) = Ap x ((f .) <$> y)
instance Apply (Ap f) where
Pure f <.> y = fmap f y
Ap x y <.> z = Ap x (flip <$> y <.> z)
instance Applicative (Ap f) where
pure = Pure
Pure f <*> y = fmap f y
Ap x y <*> z = Ap x (flip <$> y <*> z)
@jlgarhdezjlgarhdez HaskellMADpepegar
Free applicatives
@jlgarhdez
Now let’s create a small ADT and create a Free Applicative out of it!
import Control.Applicative.Free
import Control.Applicative
type Author = String
type Post = String
type Id = String
data BlogF a where
GetPost :: Id -> BlogF Post
GetAuthor :: Id -> BlogF Author
type Blog a = Ap BlogF a
jlgarhdez HaskellMADpepegar
And a bit of syntax…
getPost :: Id -> Blog Post
getPost id = liftAp $ GetPost id
getAuthor :: Id -> Blog Author
getAuthor id = liftAp $ GetAuthor id
Free applicatives
@jlgarhdezjlgarhdez HaskellMADpepegar
Free applicatives
@jlgarhdez
Now, we might want to render a page of our blog:
data Page = Page {
post :: Post,
author :: Author
} deriving Show
jlgarhdez HaskellMADpepegar
Now, let’s do Applicative magic!
jlgarhdez HaskellMADpepegar
Free applicatives
@jlgarhdez
With all we have already learned, how would you implement the following?
renderPage :: Id -> Id -> Blog Page
jlgarhdez HaskellMADpepegar
Free applicatives
@jlgarhdez
With all we have already learned, how would you implement the following?
renderPage :: Id -> Id -> Blog Page
renderPage postId authorId = Page <$> getPost postId
<*> getAuthor authorId
jlgarhdez HaskellMADpepegar
jlgarhdez HaskellMADpepegar
But we are not doing anything in that renderPage function
jlgarhdez HaskellMADpepegar
Let’s do, let’s interpret!
jlgarhdez HaskellMADpepegar
Free applicatives
@jlgarhdez
First, we need an interpreter. This interpreter is called Natural Transformation in Category Theory, and transforms a value `f a` into a `g a`.
interpIO :: BlogF a -> IO a
interpIO (GetPost id) = putStrLn ("getting post " ++ show id ++ " from DB") *> pure "this is the post"
interpIO (GetAuthor id) = putStrLn ("getting author " ++ show id ++ " from DB") *> pure "Pepe García"
jlgarhdez HaskellMADpepegar
Free applicatives
@jlgarhdez
And, last but not least, wire everything together:
main :: IO ()
main = do
page <- runAp interpIO $ renderPage 1 1
print page
jlgarhdez HaskellMADpepegar
Abstract Syntax TreesAs you know, the most important part of functional programming is referential
transparency. This means creating values, not executing effects.
That’s what our free applicatives does, they create little programs, or Abstract Syntax Trees
jlgarhdez HaskellMADpepegar
Applicatives ASTAs you imagine from our implementation, this Blog does nothing, just create values. It does not go to the DB to fetch users/posts/whatever.
It just creates what is called an Abstract Syntax Tree
jlgarhdez HaskellMADpepegar
Monadic ASTAnd, if we compare this AST with the one created by its monadic counterpart, implemented in terms of monadic bind, we can see something interesting.
We need to evaluate getPost 1 in order to evaluate getAuthor 1
jlgarhdez HaskellMADpepegar
Applicative AST. Static Analysis
@jlgarhdez
Applicative functors’ ASTs allow you to explore them from the top down without
evaluating a single line of code! This technique is called static analysis, and is
awesome for:
● Optimizing performance (deduplicate requests, in a series of HTTP requests)
● Calculate dependencies automatically
● Lint your code
jlgarhdez HaskellMADpepegar
Static AnalysisAnalysis of a program that does not evaluate the program.
It is possible with our Applicatives!
jlgarhdez HaskellMADpepegar
Static Analysis
@jlgarhdez
To demonstrate static analysis, let’s keep with our blog example:
data BlogF a where
GetPost :: Id -> BlogF Post
GetAuthor :: Id -> BlogF Author
GetComments :: Id -> BlogF [(Comment, Author)]
type Blog a = Ap BlogF a
jlgarhdez HaskellMADpepegar
Static Analysis
@jlgarhdez
Also some convenience functions:
getPost :: Id -> Blog Post
getPost id = liftAp $ GetPost id
getAuthor :: Id -> Blog Author
getAuthor id = liftAp $ GetAuthor id
getComments :: Id -> Blog [(Comment, Author)]
getComments id = liftAp $ GetComments id
jlgarhdez HaskellMADpepegar
Static Analysis
@jlgarhdez
Then, our renderPage would look like this:
renderPage :: Id -> Id -> Blog Page
renderPage post author = Page <$> getPost post
<*> getAuthor author
<*> getComments post
jlgarhdez HaskellMADpepegar
Static Analysis
@jlgarhdez
And, as explained before, we will need to interpret the ASTinterpIO :: BlogF a -> IO a
interpIO (GetPost id) = putStrLn ("getting post " ++ show id ++ " from DB") *> pure "this is the
post"
interpIO (GetAuthor id) = putStrLn ("getting author " ++ show id ++ " from DB") *> pure "@pepe"
interpIO (GetComments id) = putStrLn ("getting comments for post " ++ show id ++ " from DB")
*> pure [
("this post rocks" , "@anler"),
("you're right, @anler" , "@lorenzo"),
("Oh boy, I love haskell so bad!" , "@dani"),
("Indeed, Haskell is better than Erlang!" , "@joseluis")
]
jlgarhdez HaskellMADpepegar
Static Analysis
@jlgarhdez
But, the most amazing thing is that we can calculate the number of operations that
our renderProgram does, for example:instance Monoid Int where
mempty = 0
mappend = (+)
countInstructions :: BlogF a -> Int
countInstructions _ = 1
jlgarhdez HaskellMADpepegar
Static Analysis
@jlgarhdez
But, the most amazing thing is that we can calculate the number of operations that
our renderProgram does, for example:main :: IO ()
main = do
putStrLn "NUMBER OF REQUESTS TO THE DB:"
print instructions
putStrLn ""
putStrLn "PAGE RENDERING:"
page <- runAp interpIO page
print page
where instructions = runAp_ countInstructions page
page = renderPage 1 1
jlgarhdez HaskellMADpepegar
Composing Applicative Functors
Unlike Monads, our Applicatives are closed on composition. This means that you can provide an instance of Applicative for the product of any other two applicatives!
jlgarhdez HaskellMADpepegar
Composing Applicative Functors
@jlgarhdez
data Product m n a = Product {
first :: m a,
second :: n a
}
instance (Functor m, Functor n) => Functor (Product m n) where
fmap f fa = Product (f <$> first fa) (f <$> second fa)
instance (Applicative m, Applicative n) => Applicative (Product m n) where
pure x = Product (pure x) (pure x)
mf <*> mx = Product (first mf <*> first mx) (second mf <*> second mx)
jlgarhdez HaskellMADpepegar
Applicative Do NotationCreated at Facebook, ApplicativeDo extension allows you to write Applicative Code in a more familiar fashion, if you come from imperative programming.
jlgarhdez HaskellMADpepegar
{-# LANGUAGE ApplicativeDo #-}
renderPage :: Id -> Id -> Blog Page
renderPage postId authorId = do
post <- getPost postId
author <- getAuthor authorId
return $ Page post author
ApplicativeDo
@jlgarhdezjlgarhdez HaskellMADpepegar
Recap
jlgarhdez HaskellMADpepegar
Recap● Use more applicatives, they DO ROCK!● Always separate domain modelling from interpretation (with free applicatives)● Eliminate boilerplate with meta-programming (with static analysis)● Keep writing your code in do-block style (with ApplicativeDo)● Have fun!● Bonus point: Convince your boss to use Haskell!
jlgarhdez HaskellMADpepegar
Bibliography● Idioms: applicative programming with effects. McBride, Paterson● The Essence of the Iterator Pattern. Gibbons, Oliveira● Origami programming. Gibbons● Static Analysis with Applicatives. Gergő Érdi
jlgarhdez HaskellMADpepegar