No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a...

19
FUNCTIONAL PROGRAMMING NO.12 MONAD PARSER Tatsuya Hagino [email protected] 1

Transcript of No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a...

Page 1: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

FUNCTIONAL PROGRAMMING

NO.12 MONAD PARSER

Tatsuya Hagino

[email protected]

1

Page 2: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Monad Parser

• Make Parser a monad, and write a parser more monadic

way.

2

newtype Parser a = P (String -> Maybe (a, String))

• In order to make Parser a monad, it needs to take a type variable.

• P is the data constructor.

• A parser takes a string and it may return a value and a string. The latter string is the string not used.

• Maybe is used because a parser may fail.

input string

string left

value

parser

Maybe

Page 3: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Using Parser• Since a parser cannot be used directly, define a function to

parse a string for a given parser.

3

newtype Parser a = P (String -> Maybe (a, String))

parse::Parser a -> String -> Maybe (a, String)

parse (P p) cs = p cs

• parse is a just a function which applies the parser.

• For example, a parser which parse only one character may be written as follows:

parseOne::Parser Char

parseOne = P p

where p [] = Nothing

p (c:cs) = Just (c, cs)

• Try it out.

> parse ParseOne "123"

Just ('1',"23")

Page 4: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Functor Parser

• Before making it a monad, it need to be an instance of Functor.

• Functor f needs fmap class method:

• fmap::(a -> b) -> f a -> f b

4

import Control.Applicative

instance Functor Parser where

fmap f p = P (∖cs -> do (v, cs1) <- parse p cs

return (f v, cs1))

• fmap of Parser applies the function to the parsed result.

• parseChar::Parser Char

• fmap isDigit parseChar::Parser Bool

> parse (fmap isDigit ParseOne) "123"

Just (True,"23")

Page 5: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Applicative Parser

• Next, make it an instance of Applicative.

• Applicative f needs two class methods:• pure::a -> f a

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

5

instance Applicative Parser where

pure v = P (∖cs -> return (v, cs))

p <*> q = P (∖cs -> do (f, cs1) <- parse p cs

(v, cs2) <- parse q cs1

return (f v, cs2))

• As a parser, pure does not parse anything.

• <*> applies two parsers and applies the result of the second parser to the result of the first one.

• pure and <*> of Applicative have to satisify the following rules:• pure id <*> v = v

• pure (.) <*> u <*> v <*> w = u <*> (v <*> w)

• pure f <*> pure x = pure (f x)

• u <*> pure y = pure ($ y) <*> u

Page 6: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Monad Parser

• Now make the parser an instance of Monad.

• Monad m needs two class methods:• return::a -> m a

• (>>=)::m a -> (a -> m b) -> m b

6

instance Monad Parser where

p >>= f = P (∖cs -> do (v, cs1) <- parse p cs

parse (f v) cs1)

return x = P (∖cs -> return (x, cs))

• return is the same as pure of Applicative.

• p >>= f is a parser where if p successfully parses, the result is applied to f and continue the next parse.

• If p fails (i.e. returns Nothing), f is not used.

• Using Monad do expression, codes may become more readable.• p >>= (∖x -> q)• do { x <- p; q }

Page 7: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Alternative Parser

• Furthermore, by making the parser an instance of Alternative, parser writing becomes much easier.

• Alternative f needs two class methods:• empty::f a

• (<|>)::f a -> f a -> f a

7

instance Alternative Parser where

empty = P (∖cs -> Nothing)

p <|> q = P (∖cs -> parse p cs <|> parse q cs)

• empty fails to parse.

• p <|> q is the alternative parser where if p succeeds, its result is used, otherwise, tries q.• Maybe is also an instance of Alternative.

• some and many are defined.• some::f a -> f [a]

• many::f a -> f [a]

• some and many tries to apply the parser repeatedly. some requires at least one successful parsing, but many does not.

• Alternative instance is a monoid by empty as an identity element and <|>as an associative binary operator.

Page 8: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Parser Parts (1)• Just get one character.

8

parseOne::Parser Char

parseOne = P p

where p [] = Nothing

p (c:cs) = Just (c, cs)

• parse parseOne "abc"

⇒ Just ('a', "bc")

• parse parseOne ""

⇒ Nothing

• Check the first character satisfies a condition or not.

parseSat::(Char -> Bool) -> Parser Char

parseSat f = do x <- parseOne

if f x then return x else empty

• parse (parseSat isDigit) "123abc"

⇒ Just ('1', "23abc")

• parse (parseSat isDigit) "abc"

⇒ Nothing

Page 9: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Parser Parts (2)• Parse one character.

• Check the first character is the specified one or not.

9

parseChar::Char -> Parser Char

parseChar x = parseSat (== x)

• parse (parseChar 'a') "abc"

⇒ Just ('a', "bc")

• parse (parseChar 'a') "123"

⇒ Nothing

• Parse a string by repeatedly applying parseChar.parseString::String -> Parser String

parseString [] = return []

parseString (x:xs) = do parseChar x

parseString xs

return (x:xs)

• parse (parseString "abc") "abcab"

⇒ Just ("abc", "ab")

• parse (parseString "abc") "ababc"

⇒ Nothing

Page 10: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Parser Parts (3)• Skip spaces.

10

parseSpace::Parser ()

parseSpace = do many (parseSat isSpace)

return ()

• parse parseSpace " 123"

⇒ Just ((), "123")

• parse parseSpace "123"

⇒ Just ((), "123")

• Parse a number.

parseNum::Parser Int

parseNum = do parseSpace

cs <- some (parseSat isDigit)

return (read cs)

• parse parseNat " 123 + 567"

⇒ Just (123, " + 567")

• Skip the head spaces.

Page 11: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Parser Parts (4)• Parse a symbol.

11

parseSymbol::String -> Parser String

parseSymbol xs = do parseSpace

parseString xs

• parse (parseSymbol "*") " * 123"

⇒ Just ("*", " 123")

• parse (parseSymbol "*") " + 123"

⇒ Nothing

• Using parseNum and parseSymbol, various parsing is possible.

do x <- parseNum

parseSymbol "*"

y <- parseNum

return (x * y)

parseSymbol "*" <|> parseSymbol "+"

Page 12: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Expression Parser (1)• For simplicity, let parsers do not create parse trees but directly calculate

the result.• parseExpr::Parser Int

• parseTerm::Parser Int

• parseFactor::Parser Int

• The syntax of factor is:• factor ::= number | "(" expr ")“

It may start with a number or a left parenthesis.

12

parseFactor::Parser Int

parseFactor = parseNum

<|>

do parseSymbol "("

x <- parseExpr

parseSymbol ")"

return x

Page 13: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Expression Parser (2)• The syntax of term is:

• term ::= factor (("*" | "/") factor)*

It first parses a factor, then check whether it is followed by *or / .

13

parseTerm::Parser Int

parseTerm = parseFactor >>= nextFactor

where nextFactor x = do parseSymbol "*"

y <- parseFactor

nextFactor (x * y)

<|>

do parseSymbol "/"

y <- parseFactor

nextFactor (x `div` y)

<|>

return x

Page 14: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Expression Parser (3)• The syntax of expr is:

• expr ::= term (("+" | "-") term)*

It first parses a term, then check whether it is followed by +or - .

14

parseExpr::Parser Int

parseExpr = parseTerm >>= nextTerm

where nextTerm x = do parseSymbol "+"

y <- parseTerm

nextTerm (x + y)

<|>

do parseSymbol "-"

y <- parseTerm

nextTerm (x - y)

<|>

return x

Page 15: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Output the Result• Separate the input to lines, parse each line and output the

result.

15

showResult::Maybe (Int, String) -> String

showResult (Just (x, [])) = show x

showResult _ = "error"

main::IO ()

main = do cs <- getContents

putStr $

unlines $

map (showResult . parse parseExpr) $

lines cs

Page 16: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Parser (1)

16

import Control.Applicative

import Data.Char

newtype Parser a = P (String -> Maybe (a, String))

parse::Parser a -> String -> Maybe (a, String)

parse (P p) cs = p cs

instance Functor Parser where

fmap f p = P (∖cs -> do (v, cs1) <- parse p cs

return (f v, cs1))

instance Applicative Parser where

pure v = P (∖cs -> return (v, cs))

p <*> q = P (∖cs -> do (f, cs1) <- parse p cs

(v, cs2) <- parse q cs1

return (f v, cs2))

instance Monad Parser where

p >>= f = P (∖cs -> do (v, cs1) <- parse p cs

parse (f v) cs1)

return x = P (∖cs -> return (x, cs))

instance Alternative Parser where

empty = P (∖cs -> Nothing)

p <|> q = P (∖cs -> parse p cs <|> parse q cs)

parseOne::Parser Char

parseOne = P p

where p [] = Nothing

p (c:cs) = Just (c, cs)

parseSat::(Char -> Bool) -> Parser Char

parseSat f = do x <- parseOne

if f x then return x else empty

parseChar::Char -> Parser Char

parseChar x = parseSat (== x)

parseString::String -> Parser String

parseString [] = return []

parseString (x:xs) = do parseChar x

parseString xs

return (x:xs)

parseSpace::Parser ()

parseSpace = do many (parseSat isSpace)

return ()

parseNum::Parser Int

parseNum = do parseSpace

cs <- some (parseSat isDigit)

return (read cs)

parseSymbol::String -> Parser String

parseSymbol xs = do parseSpace

parseString xs

calcmp.hs

Page 17: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Parser (2)

17

parseFactor::Parser Int

parseFactor = parseNum

<|>

do parseSymbol "("

x <- parseExpr

parseSymbol ")"

return x

parseTerm::Parser Int

parseTerm = parseFactor >>= nextFactor

where nextFactor x = do parseSymbol "*"

y <- parseFactor

nextFactor (x * y)

<|>

do parseSymbol "/"

y <- parseFactor

nextFactor (x `div` y)

<|>

return x

parseExpr::Parser Int

parseExpr = parseTerm >>= nextTerm

where nextTerm x = do parseSymbol "+"

y <- parseTerm

nextTerm (x + y)

<|>

do parseSymbol "-"

y <- parseTerm

nextTerm (x - y)

<|>

return x

showResult::Maybe (Int, String) -> String

showResult (Just (x, [])) = show x

showResult _ = "error"

main::IO ()

main = do cs <- getContents

putStr $

unlines $

map (showResult . parse parseExpr) $

lines cs

% ./calcmp

1+2

3

(1+2)*(3+4)

21

1+2*3-4/5

7

1 2

error

1+x-5

error

実行例

Page 18: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Exercise 12

• Change parseExpr, parseTerm and parseFactor to return ParseTree rather and Int.• parseExpr::Parser ParseTree

• parseTerm::Parser ParseTree

• parseFactor::Parser ParseTree

where ParseTree is:

18

data ParseTree = Number Int |

Plus ParseTree ParseTree |

Minus ParseTree ParseTree |

Time ParseTree ParseTree |

Divide ParseTree ParseTree

Page 19: No.12 Monad Parserweb.sfc.keio.ac.jp/~hagino/fp17/12.pdf · Functor Parser •Before making it a monad, it need to be an instance of Functor. •Functor fneeds fmapclass method: •fmap::(a

Exercise 12 (cont.)

• main and showResult should be converted as follows:

19

parseFactor::Parser Int

parseFactor = parseNum

<|>

do parseSymbol "("

x <- parseExpr

parseSymbol ")"

return x

parseFactor::Parser ParseTree

parseFactor = do n <- parseNum

return (Number n)

<|>

do parseSymbol "("

x <- parseExpr

parseSymbol ")"

return x

main::IO ()

main = do cs <- getContents

putStr $

unlines $

map (showResult . parse (fmap eval parseExpr)) $

lines cs

• parseFactor should be converted as follows: