applicative style programming

59
Applicative style programming jlgarhdez HaskellMAD pepegar

Upload: jose-luis-garcia-hernandez

Post on 14-Apr-2017

340 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Applicative style programming

Applicative style programming

jlgarhdez HaskellMADpepegar

Page 2: Applicative style programming

Pepe García

Scala Engineer @ Fidesmo

Co-organizer @ HaskellMad & FP-MAD

Scala fan

Haskell Acolyth

Functional Programming Devotee

jlgarhdez HaskellMADpepegar

Page 3: Applicative style programming

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

Page 4: Applicative style programming

What are we learning today?

● What are applicatives?● When do we use them?● Who is using them?● How to use them?

jlgarhdez HaskellMADpepegar

Page 5: Applicative style programming
Page 6: Applicative style programming

What are applicatives?

Applicatives are an abstraction between functions and monads. And they open the doors for the so called applicative style!

jlgarhdez HaskellMADpepegar

Page 7: Applicative style programming

What are applicatives?

@jlgarhdez

class Functor f => Applicative f where

pure :: a -> f a

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

jlgarhdez HaskellMADpepegar

Page 8: Applicative style programming

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

Page 9: Applicative style programming

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

Page 10: Applicative style programming

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

Page 11: Applicative style programming

A small example

As an example of Applicative Programming, let’s see how Applicative and Monadic styles compare:

jlgarhdez HaskellMADpepegar

Page 12: Applicative style programming

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

Page 13: Applicative style programming

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

Page 14: Applicative style programming

A small example. Applicative

validatePersonA :: String -> String -> Either String Person

validatePersonA n l = Person <$> validateName n <*> validateLastName l

jlgarhdez HaskellMADpepegar

Page 15: Applicative style programming

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

Page 16: Applicative style programming

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

Page 17: Applicative style programming

When to use applicatives?

When we need more than a functor, and less than a monad…

Sounds appealing, but WTF is this!?

jlgarhdez HaskellMADpepegar

Page 18: Applicative style programming

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

Page 19: Applicative style programming

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

Page 20: Applicative style programming

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

Page 21: Applicative style programming

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

Page 22: Applicative style programming

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

Page 23: Applicative style programming

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

Page 24: Applicative style programming

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

Page 25: Applicative style programming

Or, you can go Free!

jlgarhdez HaskellMADpepegar

Page 26: Applicative style programming

Free Applicatives

Create an applicative from any Functor

jlgarhdez HaskellMADpepegar

Page 27: Applicative style programming

Free Applicatives.

data Ap f a where

Pure :: a -> Ap f a

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

@jlgarhdezjlgarhdez HaskellMADpepegar

Page 28: Applicative style programming

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

Page 29: Applicative style programming

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

Page 30: Applicative style programming

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

Page 31: Applicative style programming

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

Page 32: Applicative style programming

Now, let’s do Applicative magic!

jlgarhdez HaskellMADpepegar

Page 33: Applicative style programming

Free applicatives

@jlgarhdez

With all we have already learned, how would you implement the following?

renderPage :: Id -> Id -> Blog Page

jlgarhdez HaskellMADpepegar

Page 34: Applicative style programming

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

Page 35: Applicative style programming

jlgarhdez HaskellMADpepegar

Page 36: Applicative style programming

But we are not doing anything in that renderPage function

jlgarhdez HaskellMADpepegar

Page 37: Applicative style programming

Let’s do, let’s interpret!

jlgarhdez HaskellMADpepegar

Page 38: Applicative style programming

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

Page 39: Applicative style programming

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

Page 40: Applicative style programming

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

Page 41: Applicative style programming

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

Page 42: Applicative style programming

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

Page 43: Applicative style programming

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

Page 44: Applicative style programming

Static AnalysisAnalysis of a program that does not evaluate the program.

It is possible with our Applicatives!

jlgarhdez HaskellMADpepegar

Page 45: Applicative style programming

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

Page 46: Applicative style programming

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

Page 47: Applicative style programming

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

Page 48: Applicative style programming

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

Page 49: Applicative style programming

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

Page 50: Applicative style programming

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

Page 51: Applicative style programming

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

Page 52: Applicative style programming

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

Page 53: Applicative style programming

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

Page 54: Applicative style programming

{-# LANGUAGE ApplicativeDo #-}

renderPage :: Id -> Id -> Blog Page

renderPage postId authorId = do

post <- getPost postId

author <- getAuthor authorId

return $ Page post author

ApplicativeDo

@jlgarhdezjlgarhdez HaskellMADpepegar

Page 55: Applicative style programming

Recap

jlgarhdez HaskellMADpepegar

Page 56: Applicative style programming

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

Page 57: Applicative style programming

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

Page 58: Applicative style programming
Page 59: Applicative style programming