Applicative, traversable, foldable

Similar documents
Applicative, traversable, foldable

College Functors, Applicatives

Trees. Solution: type TreeF a t = BinF t a t LeafF 1 point for the right kind; 1 point per constructor.

Applicative programming with effects

Lecture 4: Higher Order Functions

Harvard School of Engineering and Applied Sciences CS 152: Programming Languages

Data types à la carte

Advanced Programming Handout 7. Monads and Friends (SOE Chapter 18)

Monad Overview (3B) Young Won Lim 1/16/18

CS 457/557: Functional Languages

Monads and all that III Applicative Functors. John Hughes Chalmers University/Quviq AB

Background Type Classes (1B) Young Won Lim 6/14/18

Monads in Haskell. Nathanael Schilling. December 12, 2014

INTRODUCTION TO HASKELL

CSCE 314 Programming Languages Functors, Applicatives, and Monads

These notes are intended exclusively for the personal usage of the students of CS352 at Cal Poly Pomona. Any other usage is prohibited without

Functional Programming

CS 440: Programming Languages and Translators, Spring 2019 Mon

GADTs. Wouter Swierstra and Alejandro Serrano. Advanced functional programming - Lecture 7. [Faculty of Science Information and Computing Sciences]

Type families and data kinds

Background Type Classes (1B) Young Won Lim 6/28/18

n n Try tutorial on front page to get started! n spring13/ n Stack Overflow!

Programming with Math and Logic

Monads and all that I. Monads. John Hughes Chalmers University/Quviq AB

GADTs. Alejandro Serrano. AFP Summer School. [Faculty of Science Information and Computing Sciences]

All About Comonads (Part 1) An incomprehensible guide to the theory and practice of comonadic programming in Haskell

Programming Languages

Monad class. Example: Lambda laughter. The functional IO problem. EDAN40: Functional Programming Functors and Monads

Maybe Monad (3B) Young Won Lim 1/3/18

Haskell Overloading (1) LiU-FP2016: Lecture 8 Type Classes. Haskell Overloading (3) Haskell Overloading (2)

A general introduction to Functional Programming using Haskell

Functional Programming Mid-term exam Tuesday 3/10/2017

Accurate Step Counting

Monads. Functional Programming (CS4011) Monads

Monad Background (3A) Young Won Lim 11/18/17

Monads. COS 441 Slides 16

Idioms are oblivious, arrows are meticulous, monads are promiscuous

Applicatives Comparisons (2C) Young Won Lim 3/6/18

GADTs. Wouter Swierstra. Advanced functional programming - Lecture 7. Faculty of Science Information and Computing Sciences

Introduction to Functional Programming in Haskell 1 / 56

INDEX. Symbols & Numbers. Learn You a Haskell for Great Good! 2011 by Miran Lipovača

A Study In Monads Leif Harald Karlsen Master s Thesis Autumn 2013

Monads. Bonus lecture 2017 David Sands

Advanced Functional Programming

Monad (3A) Young Won Lim 8/9/17

All About Monads A comprehensive guide to the theory and practice of monadic programming in Haskell Version 1.1.0

Monads seen so far: IO vs Gen

Informatics 1 Functional Programming 19 Tuesday 23 November IO and Monads. Philip Wadler University of Edinburgh

Arrow Basics. Ted Cooper CS510 Winter Here s a literate introduction to arrows and review of the basic Arrow typeclasses.

CS 457/557: Functional Languages

CS 360: Programming Languages Lecture 10: Introduction to Haskell

Haske k ll An introduction to Functional functional programming using Haskell Purely Lazy Example: QuickSort in Java Example: QuickSort in Haskell

Introduction. Wouter Swierstra. Applied functional programming. Faculty of Science Information and Computing Sciences

Week 5 Tutorial Structural Induction

Haskell Introduction Lists Other Structures Data Structures. Haskell Introduction. Mark Snyder

Data types à la carte. Wouter Swierstra Dutch HUG 25/8/10

Monad Background (3A) Young Won Lim 11/8/17

Lambda calculus. Wouter Swierstra and Alejandro Serrano. Advanced functional programming - Lecture 6

An introduction introduction to functional functional programming programming using usin Haskell

Advanced Type System Features Tom Schrijvers. Leuven Haskell User Group

FUNCTIONAL PEARL Parsing Permutation Phrases

The Haskell HOP: Higher-order Programming

Informatics 1 Functional Programming Lecture 7. Map, filter, fold. Don Sannella University of Edinburgh

Modular Reifiable Matching

The fringe of a binary tree are the values in left-to-right order. For example, the fringe of the following tree:

Testing. Wouter Swierstra and Alejandro Serrano. Advanced functional programming - Lecture 2. [Faculty of Science Information and Computing Sciences]

CSCE 314 TAMU Fall CSCE 314: Programming Languages Dr. Flemming Andersen. Haskell Functions

CS 11 Haskell track: lecture 4. n This week: Monads!

Software System Design and Implementation

Haskell: From Basic to Advanced. Part 2 Type Classes, Laziness, IO, Modules

Parsing. Zhenjiang Hu. May 31, June 7, June 14, All Right Reserved. National Institute of Informatics

Edward Kmett. Iteratees, Parsec, and Monoids A PARSING TRIFECTA

CIS 194: Homework 6. Due Friday, October 17, Preface. Setup. Generics. No template file is provided for this homework.

Accurate Step Counting

Informatics 1 Functional Programming Lectures 15 and 16. IO and Monads. Don Sannella University of Edinburgh

CSCE 314 Programming Languages

Questions & Answers 28 Sept.

Polymorphism Overview (1A) Young Won Lim 2/20/18

CSCE 314 Programming Languages

Shell CSCE 314 TAMU. Higher Order Functions

CSci 450: Org. of Programming Languages Overloading and Type Classes

Maybe Monad (3B) Young Won Lim 12/21/17

Example: A Compiler Fragment (1) Example: A Compiler Fragment (2) Example: A Compiler Fragment (3)

Programming Languages 3. Definition and Proof by Induction

CMSC 330: Organization of Programming Languages

Haskell Scripts. Yan Huang

Fun with Parallel Monad Comprehensions

Type system. Type theory. Haskell type system. EDAN40: Functional Programming Types and Type Classes (revisited)

CSCE 314 Programming Languages

IO Monad (3D) Young Won Lim 1/18/18

Lecture 10 September 11, 2017

1 The smallest free number

Monad (1A) Young Won Lim 6/26/17

301AA - Advanced Programming

PROGRAMMING IN HASKELL. Chapter 2 - First Steps

The Arrow Calculus (Functional Pearl)

CIS 194: Homework 3. Due Wednesday, February 11, Interpreters. Meet SImPL

CS152: Programming Languages. Lecture 11 STLC Extensions and Related Topics. Dan Grossman Spring 2011

CITS3211 FUNCTIONAL PROGRAMMING. 7. Lazy evaluation and infinite lists

Introduction to Haskell

Transcription:

Applicative, traversable, foldable Advanced functional programming - Lecture 4 Wouter Swierstra and Alejandro Serrano 1

Beyond the monad So far, we have seen how monads define a common abstraction over many programming patterns. This kind of abstraction occurs more often in libraries. In this lecture we will cover: applicative functors foldable traversable arrows We ll motivate the need for applicative functors starting with examples. 2

Sequencing IO operations sequenceio :: [IO a] -> IO [a] sequenceio [] = return [] sequenceio (c : cs) = do x <- c xs <- sequenceio cs return (x : xs) There is nothing wrong with this code but using do notation may seem like overkill. The variable x isn t used in the rest of the computation! We would like to apply one monadic computation to another. 3

Using liftm2 The liftm2 function is defined as follows: liftm2 :: (a -> b -> c) -> m a -> m b -> m c liftm2 f ma mb = do a <- ma b <- mb return (f a b) Using liftm2 we can write: sequenceio :: [IO a] -> IO [a] sequenceio [] = return [] sequenceio (c:cs) = liftm2 (:) c (sequenceio cs) This even works for any monad, not just the IO monad. 4

More lifting functions fmap lifts functions a -> b liftm2 lifts functions a -> b -> c Do we need a liftmn for every n? 5

Time to derive liftmn! 6

Using ap The ap function is defined as follows: ap : Monad m => m (a -> b) -> m a -> m b ap mf mx = do f <- mf x <- mx return (f x) Using ap we can write: sequenceio :: [IO a] -> IO [a] sequenceio [] = return [] sequenceio (c:cs) = return (:) ap c ap sequenceio cs 7

Evaluating expressions Another example: data Expr v = Var v Val Int Add (Expr v) (Expr v) type Env v = Map v Int eval : Expr v -> Env v -> Int eval (Var v) env = lookup v env eval (Val i) env = i eval (Add l r) env = (eval l env) + (eval r env) Once again, we are passing around an environment that is only really used in the Var branch. 8

An applicative alternative const : a -> (env -> a) const x = \env -> a s : (env -> a -> b) -> (env -> a) -> (env -> b) s ef es env = (ef env) (es env) eval : Expr v -> Env v -> Int eval (Var v) = lookup v eval (Val i) = const i eval (Add l r) = const (+) s (eval l) s (eval r) 9 The s combinator lets us apply one computation expecting an environment to another.

Transposing matrices transpose :: [[a]] -> [[a]] transpose [] = repeat [] transpose (xs : xss) = zipwith (:) xs (transpose xss) Can we play the same trick and find a combinator that will apply a list of functions to a list of arguments? zapp : [a -> b] -> [a] -> [b] zapp (f : fs) (x : xs) = (f x) : (zapp fs xs) transpose (xs : xss) = repeat (:) zapp xs zapp transpose xss 10

What is the pattern? What do these functions have in common? ap : IO a -> b) -> IO a -> IO b s : (env -> a -> b) -> (env -> a) -> (env -> b) zapp : [a -> b] -> [a] -> [b] 11

Applicative (applicative functors) class (Functor f) => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b Note that Functor is a superclass of Applicative. We can also define fmap in terms of the applicative operations: (<$>) :: Functor f => (a -> b) -> f a -> f b 12

Applicative (applicative functors) class (Functor f) => Applicative f where pure :: a -> f a (<*>) :: f (a -> b) -> f a -> f b Note that Functor is a superclass of Applicative. We can also define fmap in terms of the applicative operations: (<$>) :: Functor f => (a -> b) -> f a -> f b (<$>) f fx = pure f <*> fx 12

Using Applicative operators This type class leads to a certain code style: sequenceio :: [IO a] -> IO [a] sequenceio [] = return [] sequenceio (c:cs) = (:) <$> c <*> sequenceio cs 13

Relating Applicative functors and Monads Every monad can be given an applicative functor interface. instance Monad m => Applicative m where pure :: a -> m a pure = return mf <*> mx = do f <- mf; x <- mx; return (f x) But this may not always be the right choice. For example, we have seen the zippy applicative instance for lists; using the instance arising from the list monad gives very different behaviour! But not every applicative functor is a monad 14

Monads vs. applicative functors - I (<*>) :: (Applicative f) => f (a -> b) -> f a -> f b flip (>>=) :: (Monad m) => (a -> m b) -> m a -> m b The arguments to <*> are (typically) first-order structures (that may contain higher-order data). Monadic bind is inherently higher order. With monads, subsequent actions can depend on the results of effects: depending on the character the user enters, respond differently. 15

Monads vs applicative functors - II There are more Applicative functors than there are monads; but monads are more powerful! If you have an Applicative functor, that s good; having a monad is better. If you need a monad, that s good; only needing an Applicative functor is better. With applicative functors, the structure is statically determined (and can be analyzed or optimized). Consider the following example: miffy : Monad m => m Bool -> m a -> m a -> ma 16

Imprecise but catchy slogans Monads are programmable semi-colons! Applicatives are programmable function application! 17

Applicative functor laws identity pure id <*> u = u composition pure (.) <*> u <*> v <*> w = u <*> (v <*> w) homomorphism pure f <*> pure x = pure (f x) interchange u <*> pure x = pure ( f -> f x) <*> u 18

Spot the pattern sequenceio :: [IO a] -> IO [a] sequenceio [] = pure [] sequenceio (c:cs) = (:) <$> c <*> sequenceio cs transpose :: [[a]] -> [[a]] transpose [] = pure [] transpose (xs : xss) = (:) <$> xs <*> transpose xss Both these functions take a list of applicative actions as argument. 19 They traverse this list, performing the actions one by one, collecting the results in a list.

Traversing lists We can define a new function to capture this pattern: sequence :: Applicative f => [f a] -> f [a] sequence [] = pure [] sequence (x:xs) = pure (:) <*> x <*> traverse xs Clearly we can traverse lists in this fashion but what other data types support such an operation? 20

Traversable The Traversable class captures those types that can be traversed in this fashion: class Traversable t where traverse :: Applicative f => (a -> f b) -> t a -> f (t b) sequencea :: Applicative f => t (f a) -> f (t a) It requires a slightly more general traverse than the one we have seen so far. 21

Traversable The Traversable class captures those types that can be traversed in this fashion: class Traversable t where traverse :: Applicative f => (a -> f b) -> t a -> f (t b) sequencea :: Applicative f => t (f a) -> f (t a) It requires a slightly more general traverse than the one we have seen so far. Define traverse and sequencea in terms of each other 21

Traversable with defaults class Traversable t where traverse :: Applicative f => (a -> f b) -> t a -> f (t b) traverse f = sequencea. fmap f sequencea :: Applicative f => t (f a) -> f (t a) sequencea = traverse id 22

Traversable: example data Expr v = Var v Val Int Add (Expr v) (Expr v) instance Traversable Expr where traverse :: Applicative f => (a -> f b) -> Expr a -> f (Expr b) traverse f (Var v) = Var <$> f v traverse f (Val x) = pure (Val x) traverse f (Add l r) = Add <$> traverse f l <*> traverse f r In general, traverse is just like fmap only in applicative style. 23

Introducing Foldable In the Haskell libraries, Traversable is defined slightly differently. class (Functor t, Foldable t) => Traversable t where What is the Foldable class? 24

Folding a list The foldr function on lists captures a common pattern think of it as a functional for-loop. foldr :: (a -> b -> b) -> b -> [a] -> b foldr f y [] = y foldr f y (x:xs) = f x (foldr f y xs) We can use it to define all kinds of simple list traversals: sum = foldr (+) 0 maximum = foldr max minbound xs (++) = \ys -> foldr (:) ys xs concat = foldr (++) [] map = \f -> foldr (xs -> f x : xs) [] 25

Folding: beyond lists There are many other data types that support some form of fold operator. data Tree a = Leaf a Node (Tree a) (Tree a) Empty foldtree :: (a -> b -> b) -> b -> Tree a -> b foldtree f y Empty = y foldtree f y (Leaf x) = f x y foldtree f y (Node l r) = foldtree f (foldtree f y r) l Note that generic programming gives a slightly more precise account. 26

Foldable class Foldable f where foldr :: (a -> b -> b) -> b -> f a -> b foldmap :: Monoid m => (a -> m) -> t a -> m Sometimes it can be easier to define the foldmap function but what is a Monoid? 27

Intermezzo: monoids class Monoid a where mempty :: a mappend :: a -> a -> a Here mempty should be the unit of the associative operator mappend. 28

Monoids everywhere Bool using && and True; Bool using or and False; Int using + and 0; Int using * and 1; Int using max and minbound; List a using ++ and []; Imperative programs using ; and skip; a -> a using. and id; Monoids pop up everywhere! 29

Defining foldmap Instead of defining fold, sometimes it can be easier to define foldmap: foldmap :: Monoid m => (a -> m) -> Tree a -> m foldmap f Empty = mempty foldmap f (Leaf x) = f x foldmap f (Node l r) = foldmap f l `mappend` foldmap f r You need to apply f all a values in the tree and combine subtrees using mappend. 30

Why? What is the point of all this abstraction? We all agree (I hope!) that foldr is useful for lists. sum = foldr (+) 0 maximum = foldr max minbound xs (++) ys = foldr (:) ys xs concat = foldr (++) [] map f = foldr (\xs -> f x : xs) [] 31

Why? What is the point of all this abstraction? We all agree (I hope!) that foldr is useful for lists. sum = foldr (+) 0 maximum = foldr max minbound xs (++) ys = foldr (:) ys xs concat = foldr (++) [] map f = foldr (\xs -> f x : xs) [] but we can now give definitions that work for any foldable structure. 31

Why? What is the point of all this abstraction? We all agree (I hope!) that foldr is useful for lists. sum :: Foldable f => f Int -> Int sum = foldr (+) 0 maximum :: Foldable f => f Int -> Int maximum = foldr max minbound flatten :: Foldable f => f a -> [a] flatten = foldmap (\x -> [x]) (++) 32

Generalizing any As a slightly less trivial example, consider the any function: any :: (a -> Bool) -> [a] -> Bool any p [] = False any p (x:xs) = p x any p xs How can we generalize this to work on any traversable type? 33

Mighty Booleans Let s start by finding the right monoidal structure. Instead of defining an instance for Bool, introducing a new type can help clarify the monoidal structure we are using. newtype Might = Might {might : Bool} instance Monoid Might where mempty = Might False (Might x) `mappend` (Might y) = Might (x y) 34

Generic any any :: Foldable f => (a -> Bool) -> f a -> Bool any p = might. foldmap (Might. p) Many other functions can be generalized similarly. 35

The Foldable-Traversible Proposal As of GHC 7.10, many Prelude functions have been generalized to work over any traversable structure and not just lists. Suppose we have a data type for binary trees, with the obvious traversable/foldable instances: data Tree a where Leaf :: a -> Tree a Node :: Tree a -> Tree a -> Tree a We can use the prelude functions we are used to over this data structure too. 36

Example folds over trees > let t = (Node (Leaf 1) (Node (Leaf 2) (Leaf 3)) > any iseven t True > length t 3 > elem 3 t True We no longer need to define specialized functions for trees. 37

Exercise What is the foldable instance for Maybe? What about the foldable instance for pairs? 38

Drawbacks But there are also quite surprising examples: minimum (1,1000) length (lookup 4 [(2, Hello ), (4, World ), (5,! )] null (lookup 3 []) 39

Drawbacks But there are also quite surprising examples: minimum (1,1000) length (lookup 4 [(2, Hello ), (4, World ), (5,! )] null (lookup 3 []) Sometimes code may type check, where you would have liked to see a type error. 39

Introducing arrows If applicative functors generalize the notion of application, can we find a similar abstraction over functions and function composition? Yes! There is more than a decade of work investigating functional programming using Arrows. 40

Arrows class Arrow a where arr :: (b -> c) -> a b c (>>>) :: a b c -> a c d -> a b d first :: a b c -> a (b,d) (c,d) Just like applicative functors and monads, arrows have several associated laws. Many programs using arrows require additional operations similar to classes such as MonadPlus. GHC supports special syntax for programming with Arrows, similar to the do notation for monads. 41

Historical context Monads were originally studied in the context of program language semantics. Only later, was their importance for structuring programs discovered (and subsequently, modelling IO) Arrows (Hughes 2000) were proposed as an alternative to monads, but they have not been widely adopted. More recently, applicative functors have gained a lot of traction in the Haskell community (McBride and Paterson 2008), generalising the interface by Duponcheel and Swierstra (1996). Applicative functors, together with the associated Traversable and Foldable classes, are now part of the Haskell standard. 42

Why care? Functional programmers are adicted to abstraction: as soon as they spot a pattern, they typically want to abstract over it. The type classes we have seen today, such as monads, applicative functors, foldable, and traversable, all capture some common pattern. Using these patterns can save you some boilerplate code. But understanding these patterns can help guide your design. Is my type a monad? Or is it just applicative? Can I find a Traversable instance? Why not? 43

Further reading Applicative programming with effects, McBride and Paterson Monoids: Theme and Variations, Brent Yorgey Programming with arrows, John Hughes Idioms are oblivious, arrows are meticulous, monads are promiscuous, Lindley, Wadler and Yallop and much much more! 44

Time for type trickery! 45

Functor and Foldable from Traversable class (Functor t, Foldable t) => Traversable t where traverse :: Applicative f => (a -> f b) -> t a -> f (t b) sequencea :: Applicative f => t (f a) -> f (t a) We can write fmap and foldmap using traverse or sequencea 46

Functor and Foldable from Traversable class (Functor t, Foldable t) => Traversable t where traverse :: Applicative f => (a -> f b) -> t a -> f (t b) sequencea :: Applicative f => t (f a) -> f (t a) We can write fmap and foldmap using traverse or sequencea Let s do it! 46

fmap via traverse newtype Id a = Id { getid :: a } myfmap :: Traversable f => (a -> b) -> f a -> f b myfmap f = getid. traverse (Id. f) 47

foldmap via traverse traverse :: Applicative f => (a -> f b) -> t a -> f (t b) foldmap :: Monoid m => (a -> m) -> t a -> f m 48

foldmap via traverse traverse :: Applicative f => (a -> f b) -> t a -> f (t b) foldmap :: Monoid m => (a -> m) -> t a -> f m You need to find an Applicative which behaves like a monoid myfoldmap f = getmonoidapplicative. traverse (MonoidApplicative. f) 48

foldmap via traverse using Const The functor you need is called Const data Const k a = Const { getconst :: k } instance Functor (Const k) where fmap _ (Const x) = Const x What about the Applicative instance? 49

foldmap via traverse using Const instance Monoid k => Applicative (Const k) where pure = Const mempty Const x <*> Const y = Const (x `mappend` y) 50