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

Similar documents
College Functors, Applicatives

Applicative, traversable, foldable

Applicative, traversable, foldable

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

Monads. Bonus lecture 2017 David Sands

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

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

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

Monads seen so far: IO vs Gen

CIS552: Advanced Programming

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

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

CSCE 314 Programming Languages Functors, Applicatives, and Monads

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

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

Monads. Functional Programming (CS4011) Monads

Structurally Recursive Descent Parsing. Nils Anders Danielsson (Nottingham) Joint work with Ulf Norell (Chalmers)

Fun with Parallel Monad Comprehensions

Haskell Monads CSC 131. Kim Bruce

Functional Programming

INTRODUCTION TO HASKELL

Type Checking in COOL (II) Lecture 10

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

CS 11 Haskell track: lecture 1

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

Zipping Search Trees. Tom Schrijvers. with Denoit Desouter and Bart Demoen

An introduction to functional programming. July 23, 2010

Monad Background (3A) Young Won Lim 10/5/17

CS 320: Concepts of Programming Languages

JVM ByteCode Interpreter

EECS 700 Functional Programming

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

Fusion-powered EDSLs

Syntax Matters: Writing abstract computations in F#

Type families and data kinds

Functional Programming

Programming Languages

Programming with Math and Logic

Monadic Parsing. 1 Chapter 13 in the book Programming in Haskell (2nd ed.) by. 2 Chapters 10 and 16 in the book Real World Haskell by Bryan

Module 3: New types of data

Building a Parser III. CS164 3:30-5:00 TT 10 Evans. Prof. Bodik CS 164 Lecture 6 1

Practical Haskell. An introduction to functional programming. July 21, Practical Haskell. Juan Pedro Villa-Isaza. Introduction.

Lecture 4: Higher Order Functions

COMP3141 Assignment 2

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

Types in Programming Languages Dynamic and Static Typing, Type Inference (CTM 2.8.3, EPL* 4) Abstract Data Types (CTM 3.7) Monads (GIH** 9)

Compiler construction

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

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

PIC 10A Objects/Classes

Solution sheet 1. Introduction. Exercise 1 - Types of values. Exercise 2 - Constructors

CSCE 314 Programming Languages

Advanced Type System Features Tom Schrijvers. Leuven Haskell User Group

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

State Monad (3D) Young Won Lim 8/31/17

Programming Paradigms

Modules and Representation Invariants

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

Haskell An Introduction

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

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

CS52 - Assignment 8. Due Friday 4/15 at 5:00pm.

CSCE 314 Programming Languages

Computer Programming. Basic Control Flow - Loops. Adapted from C++ for Everyone and Big C++ by Cay Horstmann, John Wiley & Sons

There are algorithms, however, that need to execute statements in some other kind of ordering depending on certain conditions.

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

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

(Co)Monads forgrad Dummies Students

1. Function Type Declarations (12 points):

An introduction introduction to functional functional programming programming using usin Haskell

Introduction to Haskell

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

Parsing a primer. Ralf Lämmel Software Languages Team University of Koblenz-Landau

CIS 194: Homework 5. Due Friday, October 3, Rings. No template file is provided for this homework. Download the

Lambda-termide redutseerimine

CS 457/557: Functional Languages

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

Compilers and computer architecture From strings to ASTs (2): context free grammars

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

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

Compilers and computer architecture: Semantic analysis

Parsing Combinators: Introduction & Tutorial

Background Constructors (1A) Young Won Lim 7/9/18

Last time: generic programming

CS115 - Module 10 - General Trees

CS1622. Semantic Analysis. The Compiler So Far. Lecture 15 Semantic Analysis. How to build symbol tables How to use them to find

Monads in Haskell. Nathanael Schilling. December 12, 2014

CS 220: Introduction to Parallel Computing. Arrays. Lecture 4

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

Monad P1 : Several Monad Types (4A) Young Won Lim 2/13/19

Part 0. A (Not So) Brief Introduction to Haskell

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

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

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

Monad Transformers Step by Step

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

Skeletons and the Anatomy of Monads

Course year Typeclasses and their instances

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

Semantics of programming languages

Transcription:

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

Recall our expression parser expr = do a <- term exactly '+' b <- term return (a+b) `mplus` term term = do a <- factor exactly '*' b <- factor return (a*b) `mplus` factor factor = number `mplus` do exactly '(' a <- expr exactly ') return a exactly t = satisfy (==t) Wouldn t it be nice to use liftm3 here? liftm3 (\a _ b -> a*b)? liftm3x_x (*)?

An Applicative Interface Let s build liftm3 from simpler parts! (<*>) :: Monad m => m (a -> b) -> m a -> m b f <*> x = liftm2 ($) f x Then liftm f x = return f <*> x liftm2 f x y = return f <*> x <*> y liftm3 f x y z = return f <*> x <*> y <*> z

Ignoring Values Variations on (<*>) that ignore one argument (<*) :: Monad m => m a -> m b -> m a a <* b = return const <*> a <*> b (*>) :: Monad m => m a -> m b -> m b a *> b = return (const id) <*> a <*> b All the effects happen left to right, but some values are discarded

Revisiting our expression parser expr = return (+) <*> term <* exactly '+ <*> term `mplus` term term = return (*) <*> factor <* exactly '* <*> factor `mplus` factor factor = number `mplus` exactly '( *> expr <* exactly ') exactly t = satisfy (==t) More concise More applicative in feel

Another Problem Backtracking is inefficient! instance MonadPlus m => MonadPlus (StateT s m) where m `mplus` m' = StateT (\s -> runstatet m s `mplus` runstatet m' s) Even if the first parser m succeeds we must keep the entire input and the other parser in memory, in case we should ever need to backtrack

A Solution? Compute static information about each parser, and use to optimise Possible starter symbols Can it match the empty string? In m `mplus` m, if m 1. Cannot match the empty string 2. Cannot match the next symbol Then it can safely be discarded

Attaching Static Information Let parsers be a pair, of Static information A dynamic parsing function (as before) But what about (>>=)? m >>= f matches We can t know? until we see the (dynamic) input! m matches and f? matches starters(m >>= f) = starters m ++ starters (f?) if m matches

Hmm (>>=) is an obstacle to computing static into But (<*>) makes (>>=) less necessary can we do without (>>=) sometimes? expr = return (+) <*> term <* exactly '+ <*> term `mplus` term Computing static info for f <*> m is unproblematic

Applicative Functors An alternative interface class Functor f => Applicative f where pure :: a -> f a (<*>) :: f (a->b) -> f a -> f b Every Monad is Applicative newtype WrappedMonad m a = Wrap {unwrap :: m a} instance Monad m => Applicative (WrappedMonad m) where pure a = Wrap (return a) Wrap f <*> Wrap x = Wrap (liftm2 ($) f x)

Not every Applicative is a Monad! Can a parser match the empty string? newtype Empty a = Empty Bool instance Applicative Empty where pure _ = Empty True Empty f <*> Empty x = Empty (f && x) A parser that can t parse just tell us if it matches! A generally useful kind of non-monadic Applicative: collect information using a monoid

But every Applicative is a Functor We can always define fmap like this fmap :: (a->b) -> f a -> f b fmap f a = pure f <*> a (We can t write a general instance, because the type-checker would use it too often, but for any specific f the definition works) instance Applicative f => Functor f where fmap f a = pure f <*> a

Applicative vs Monad Consider a conditional function cond :: m Bool -> m a -> m Effects a -> depend m a on value of m Monadic: cond m f g = do bool <- m if bool then f else g Applicative: All effects happen anyway cond m f g = pure (\b t e->if b then t OK else for parsing e) CFGs! <*> m <*> f <*> g

Applicatives are more composable! We can pair any two Applicatives: data Prod f g a = Prod (f a) (g a) instance (Applicative f, Applicative g) => Applicative (Prod f g) where pure x = Prod (pure x) (pure x) Prod f g <*> ~(Prod x y) = Prod (f <*> x) (g <*> y)

Applicatives are more composable! We can compose Applicatives: newtype Compose f g a = Comp (f (g a)) instance (Applicative f, Applicative g) => Applicative (Compose f g) where pure x = Comp (pure (pure x)) Comp f <*> Comp x = Comp (pure (<*>) <*> f <*> x) Even monads which don t compose can be wrapped and composed as Applicatives!

Making Choices We need an analogue of MonadPlus class Applicative f => Alternative f where empty :: f a (< >) :: f a -> f a -> f a Of course, wrapping a MonadPlus gives an Alternative instance MonadPlus m => Alternative (WrappedMonad m) where empty = Wrap mzero Wrap a < > Wrap b = Wrap (a `mplus` b)

Making Empty an Alternative Can we define an Alternative instance for Empty? Matches no strings, so definitely not the empty string When does a choice between parsers match? instance Alternative Empty where empty = Empty False Empty f < > Empty g = Empty (f g) Compare <*>, which used &&

Some and Many revisited Now we can define some and many for any Alternative functor! some f = s pure where (:) <*> <$> f <*> many f many s = f (:) = some <$> f < > <*> pure (s < > [] pure[]) Even generic optional values! optional f = pure Just <*> <$> f < > pure Nothing

Where are we now? Wrapping our Parser monad gives us an Alternative functor With pure, <*>, empty, < >, <*, *>, some, many Almost everything we need to write parsers! newtype Monadic a = Monadic (WrappedMonad (StateT String Maybe) a) deriving (Functor, Applicative, Alternative) We just need to add exactly

The Parser Class Because we want multiple representations of parsers, define a class class Alternative p => Parser p where exactly :: Char -> p Char Monadic implementation: instance Parser Monadic where exactly t = Monadic (WrapMonad (do ts <- get case ts of [] -> mzero t':ts' -> do guard (t==t') put ts' return t))

Our Example, Applicatively number, expr, term, factor :: Parser p => p Integer number = read <$> some (anyof ['0'..'9']) expr = (+) <$> term <* exactly '+' <*> term < > term term = (*) <$> factor <* exactly '*' <*> factor < > factor factor = number < > exactly '(' *> expr <* exactly ')' *Parser> runmonadic expr "1+2*3" Just (7,"")

Empty Parser Can exactly t match the empty string? Examples instance Parser Empty where exactly _ = Empty False *Parser> runempty expr False *Parser> runempty (many expr) True We can execute and analyse the same code

What tokens can a parse start with? newtype Starts a = Starts [Char] instance Functor Starts where fmap f x = pure f <*> x instance Applicative Starts where pure x = Starts [] Starts ts <*> Starts ts =??? May start with one of ts if the first parser matches instance Alternative Starts where empty = Starts [] Starts ts < > Starts ts' = Starts (nub (ts++ts')) instance Parser Starts where exactly t = Starts [t]

Of course this doesn t work *Parser> runstarts (exactly 'x' < > exactly 'y') "xy *Parser> runstarts (some (exactly 'x')) "*** Exception: No instance nor default method for class operation Control.Applicative.<*> As soon as we use something needing <*>, we crash

Let s compute Empty and Starts together Just form their product newtype Static a = Static (Prod Starts Empty a) deriving (Functor,Applicative,Alternative,Parser) We ll need to make Prod a Parser instance (Parser f, Parser g) => Parser (Prod f g) where exactly t = Prod (exactly t) (exactly t) Of course, it still doesn t work! *Parser> runstatic (exactly 'x' < > exactly 'y') ("xy",false) *Parser> runstatic (some (exactly 'x')) ("*** Exception: No instance nor default method for class operation Control.Applicative.<*>

Replace <*> just for Static! Derive everything except Applicative newtype Static a = Static (Prod Starts Empty a) deriving (Functor,Alternative,Parser) instance Applicative Static where pure x = Static (pure x) Static (Prod (Starts ts) (Empty e)) <*> ~(Static (Prod (Starts ts') (Empty e'))) = Static (Prod (Starts (ts++if e then ts' else [])) (Empty e<*>empty e'))

Now it works! Examples: *Parser> runstatic (some (exactly ' ') *> exactly 'x') (" ",False) *Parser> runstatic (many (exactly ' ') *> exactly 'x') (" x",false) *Parser> runstatic expr ("0123456789(",False)

(Truth in Advertising) It should work, but it doesn t I have to explicitly declare the Alternative instance too, and work around a bug in ghc s strictness analyser (?)

Optimizing < > Choice is inefficient in backtracking parsers Let s pair the Static and Monadic parsers newtype OptParser a = Opt (Prod Static Monadic a) deriving (Functor, Applicative, Parser) Define an Alternative instance that optimizes < > based on the starter tokens and the next character Could not be done with monads

What else can we do? Let s try Applicative randomness! newtype Random a = Random (WrappedMonad RandomM a) deriving (Functor, Applicative, Choice) We need a class for choose class Applicative f => Choice f where choose :: Int -> Int -> f Int instance Choice (WrappedMonad RandomM) where choose m n m <= n = WrapMonad (do x <- generate return (m + (x `mod` (n-m+1))))

Random Alternatives We make the choice in the monad to avoid generating both alternatives always instance Alternative Random where Random (WrapMonad m) < > Random (WrapMonad m') = Random (WrapMonad (do x <- generate if even x then m else m')) BUT No sensible definition of empty

Bounded lists Bounded lists are easy to define with < >: blist 0 g = pure [] blist n g n > 0 = shorter < > (:) <$> g <*> shorter where shorter = blist (n-1) g *Random> runrandom (blist 30 (choose 1 10 < > pure 33)) [33,5,33,5,33,33,9,4,33,7,3,33,1,10] But do we really want 33 so often?

Cardinality How many possibilities are we choosing from? newtype Card a = Card {runcard :: Integer} instance Applicative Card where pure _ = Card 1 Card m <*> Card n = Card (m*n) instance Alternative Card where empty = Card 0 Card m < > Card n = Card (m+n) instance Choice Card where choose m n = Card (fromintegral $ n-m+1)

Use Cardinality to Guide Choice Compose Card and Random into a product newtype Uniform a = Uniform (Prod Card (WrappedMonad RandomM) a) deriving (Functor, Applicative, Choice) instance (Choice f, Choice g) => Choice (Prod f g) where choose m n = Prod (choose m n) (choose m n) Define Alternative Uniform to use cardinalities as weights! empty = Uniform (Prod empty undefined)

That s Better! Here s the old test *Random> runrandom (blist 30 (choose 1 10 < > pure 33)) [33,5,33,5,33,33,9,4,33,7,3,33,1,10] Lots of 33s! Here s the new one *Random> rununiform (blist 30 (choose 1 10 < > pure 33)) [5,33,2,6,7,3,3,7,10,7,1,10,4,10,9,4,3,6,4,6,10,3,33,5,3, 33,9,1,4]

What else can we do? ZipLists! [f,g,h] <*> [x,y,z] [f x,g y,h z] Think of a sequence of steps Lists are already Applicative (all combinations), so we need a new type

Applicative ZipLists instance Applicative ZipList where pure x = ZipList (repeat x) ZipList fs <*> ZipList xs = ZipList (zipwith ($) fs xs) It makes sense that pure repeats x infinitely it s available at every step

A ZipList Monad? Consider [a 1,a 2 a n ] >>= f a 1 a 2 a n f [b 11,b 12 b 1m ] [b 21,b 22 b 2m ] [b n1,b n2 b nm ] The 3rd monad law fails (if f returns lists of different lengths) Would also be very inefficient

Functional Reactive Programming Describes changing behaviours over time Behaviour a Time -> a Naturally applicative! Behaviour (a->b) -> Behaviour a -> Behaviour b Inefficient as a monad! Terrible for GC! Behaviour a -> (a -> Behaviour b) -> Behaviour b Construct a Behaviour b from a n at each Time, then sample it at one point!

Html (nano-)formlets Example: Names must be unique Generated by: Name: <input type="text" name="name"> <br> Age: <input type="text" name="age"> <br> Gender: <input type="text" name="gender"> Data returns to the application as [( name, John Hughes ), ( age, 54 ), ( gender, male )] Names must match

Using Formlets data Person = Person String Integer Gender deriving Show data Gender = Male Female deriving (Read, Show) person = Person <$ html "Name: " <*> input <* html "<br>\nage: " <*> (read <$> input) <* html "<br>\ngender: " <*> (read <$> input) Generate HTML Accept and process input

The features we need Generation of unique names Collection of generated HTML Evaluation of results given field values in this order! newtype Formlet a = Formlet (Compose NameGen (Compose Html Eval) a) deriving (Functor, Applicative) NameGen (Html (Eval a)) Staged effects

Name Generation We use a state monad to carry a counter newtype NameGen a = NameGen (WrappedMonad (State Integer) a) deriving (Functor, Applicative) Generate a name by incrementing it nextname :: NameGen String nextname = NameGen (WrapMonad (do n <- get put (n+1) return ("input_"++show n)))

Collecting Html Collect a string of HTML as the effect newtype Html a = Html (String,a) deriving (Functor, Applicative) Basic operation generates some text text :: String -> Html () text s = Html (s,()) Generating a named input field inputfield name = text $ "<input type=\"text\"name=\""++name++"\">"

Evaluation of fields Pass in list of fields implicitly newtype Eval a = Eval ([(String,String)] -> a) deriving (Functor, Applicative) An operation to look up the value of a named field field :: String -> Eval String field name = Eval (fromjust. lookup name)

Formlets: Generating HTML newtype Formlet a = Formlet (Compose NameGen (Compose Html Eval) a) deriving (Functor, Applicative) html :: String -> Formlet () html s = Formlet (Comp (pure (Comp (pure <$> text s)))) Html () Html (Eval ()) Compose Html Eval () NameGen (Compose Html Eval ()) Compose NameGen (Compose Html Eval) ()

Formlets: Input Fields Combine effects in all three Applicatives! input :: Formlet String input = Formlet (Comp ( (\name -> Comp ((pure (field name)) <* inputfield name) ) <$> nextname )) Key: NameGen Html Eval

Running it Run the person Formlet *Formlet> let (output,fun) = runformlet person Print the HTML *Formlet> putstrln output Name: <input type="text" name="input_1"><br> Age: <input type="text" name="input_2"><br> Gender: <input type="text" name="input_3"> Evaluate on corresponding inputs *Formlet> runeval fun [("input_1","john Hughes"), ("input_2","54"), ("input_3","male")] Person "John Hughes" 54 Male

Conclusions Applicative functors are Less powerful than monads less expressive More general than monads more instances More composable than monads Prod and Compose No need for Applicative transformers Enjoy a simple interface a sweet spot in common interfaces Have lots of applications