transform your state \/ err

142
@gerferra Transform your State \/ Err Transform your State \/ Err An example of functional parsing with state and error handling An example of functional parsing with state and error handling @gerferra

Upload: german-ferrari

Post on 20-Jul-2015

854 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Transform your State \/ Err

@gerferra

Transform your State \/ Err

Transform your State \/ Err

An example of functional parsing with state and error handling

An example of functional parsing with state and error handling

@gerferra

Page 2: Transform your State \/ Err

Initial language

Arithmetic expressions over integers

1;

2+2;

3*3+4*4*(1+2);

Page 3: Transform your State \/ Err

Functional parsing

Write a parser using “just functions” ...

… within some programming language

… in the form of some embedded DSL

Page 4: Transform your State \/ Err

Functional parsingAll of this has a proper name:

Parser combinators

A collection of basic parsing functions that recognise a piece of input

A collection of combinators that build new parsers out of existing oneshttp://www.fing.edu.uy/inco/cursos/pfa/uploads/Main/parsercombinators

Page 5: Transform your State \/ Err

Parser combinatorsScala has many parsing combinator libraries

Two common ones:

scala-parser-combinators

parboiled2

Page 6: Transform your State \/ Err

Parser combinatorsScala has many parsing combinator libraries

Two common ones:

scala-parser-combinators

parboiled2

The one we will be using

Page 7: Transform your State \/ Err

Parser combinatorsScala has many parsing combinator libraries

Two common ones:

scala-parser-combinators

parboiled2 Not really parser combinators ...

but very fast!

Page 8: Transform your State \/ Err

Parser combinatorsScala has many parsing combinator libraries

Two common ones:

scala-parser-combinators

parboiled2

New kid on the block: lihaoyi/fastparse

Page 9: Transform your State \/ Err

scala-parser-combinators

Part of the Scala standard library but distributed separately

libraryDependencies += "org.scala-lang.modules" %% "scala-parser-combinators" % "1.0.4"

Page 10: Transform your State \/ Err

scala-parser-combinators

Slow but good enough in this case

The first (?) parser combinator library for Scala

Many (but not all) of the other parser combinator libraries use similar names/symbols for the combinators

Page 11: Transform your State \/ Err

scala-parser-combinatorsimport scala.util.parsing.combinator._

object BasicCombinators extends Parsers

with RegexParsers {

def pA : Parser[Char] = 'A' def pB : Parser[Char] = 'B' def pAlt: Parser[Char] = pA | pB def pSeq: Parser[Char ~ Char] = pA ~ pB def pRep: Parser[List[Char]] = rep(pA) def pOpt: Parser[Option[Char]] = opt(pA)}

Page 12: Transform your State \/ Err

import scala.util.parsing.combinator._

object BasicCombinators extends Parsers

with RegexParsers {

def pA : Parser[Char] = 'A' def pB : Parser[Char] = 'B' def pAlt: Parser[Char] = pA | pB def pSeq: Parser[Char ~ Char] = pA ~ pB def pRep: Parser[List[Char]] = rep(pA) def pOpt: Parser[Option[Char]] = opt(pA)}

scala-parser-combinatorsBase type

Page 13: Transform your State \/ Err

import scala.util.parsing.combinator._

object BasicCombinators extends Parsers

with RegexParsers {

def pA : Parser[Char] = 'A' def pB : Parser[Char] = 'B' def pAlt: Parser[Char] = pA | pB def pSeq: Parser[Char ~ Char] = pA ~ pB def pRep: Parser[List[Char]] = rep(pA) def pOpt: Parser[Option[Char]] = opt(pA)}

scala-parser-combinators

Useful extensions

Page 14: Transform your State \/ Err

import scala.util.parsing.combinator._

object BasicCombinators extends Parsers

with RegexParsers {

def pA : Parser[Char] = 'A' def pB : Parser[Char] = 'B' def pAlt: Parser[Char] = pA | pB def pSeq: Parser[Char ~ Char] = pA ~ pB def pRep: Parser[List[Char]] = rep(pA) def pOpt: Parser[Option[Char]] = opt(pA)}

scala-parser-combinators

alternative, sequence, repetition, optionals ...

Page 15: Transform your State \/ Err

scala-parser-combinators

trait Parsers { type Input

sealed trait ParseResult[+T]

trait Parser[+T] extends (Input => ParseResult[T])}

Page 16: Transform your State \/ Err

scala-parser-combinators

trait Parsers { type Input

sealed trait ParseResult[+T]

trait Parser[+T] extends (Input => ParseResult[T])}

Success[+T] | NoSuccess...plus the input not consumed

Page 17: Transform your State \/ Err

scala-parser-combinators

trait Parsers { type Input

sealed trait ParseResult[+T]

trait Parser[+T] extends (Input => ParseResult[T])}

Success[+T] | NoSuccess...plus the input not consumed

~ Input => (T, Input)

Page 18: Transform your State \/ Err

scala-parser-combinators

trait Parsers { type Input

sealed trait ParseResult[+T]

trait Parser[+T] extends (Input => ParseResult[T])} A Parser is a function taking some input and

returning either some value of type T, or a failure

Success[+T] | NoSuccess...plus the input not consumed

Page 19: Transform your State \/ Err

scala-parser-combinators

trait Parsers { type Input

sealed trait ParseResult[+T]

trait Parser[+T] extends (Input => ParseResult[T])} A Parser is a function taking some input and

returning either some value of type T, or a failure

Success[+T] | NoSuccess...plus the input not consumed

Page 20: Transform your State \/ Err

Returning to our language

1;

2+2;

3*3+4*4*(1+2);

Page 21: Transform your State \/ Err

Returning to our languageobject Parser1 extends RegexParsers {

def pL1: Parser[List[Int]] = rep(pExpr <~ ";")

def pExpr = pAdd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom

def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}

Page 22: Transform your State \/ Err

Returning to our languageobject Parser1 extends RegexParsers {

def pL1: Parser[List[Int]] = rep(pExpr <~ ";")

def pExpr = pAdd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom

def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}

Many things going on

Page 23: Transform your State \/ Err

Returning to our languageobject Parser1 extends RegexParsers {

def pL1: Parser[List[Int]] = rep(pExpr <~ ";")

def pExpr = pAdd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom

def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}

We are evaluating the result directly, avoiding construct an AST

Page 24: Transform your State \/ Err

Returning to our languageobject Parser1 extends RegexParsers {

def pL1: Parser[List[Int]] = rep(pExpr <~ ";")

def pExpr = pAdd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom

def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}

Precedence rules are encoded in the grammar

Page 25: Transform your State \/ Err

Returning to our languageobject Parser1 extends RegexParsers {

def pL1: Parser[List[Int]] = rep(pExpr <~ ";")

def pExpr = pAdd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom

def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}

This is evaluating from right to left.

If you need to control associativity, the rep combinator is your friend.

Page 26: Transform your State \/ Err

Returning to our languageobject Parser1 extends RegexParsers {

def pL1: Parser[List[Int]] = rep(pExpr <~ ";")

def pExpr = pAdd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom

def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}

Regular expressions are converted to Parsers implicitly

Page 27: Transform your State \/ Err

Returning to our languageobject Parser1 extends RegexParsers {

def pL1: Parser[List[Int]] = rep(pExpr <~ ";")

def pExpr = pAdd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom

def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}

^^ is an alias of the map function

Page 28: Transform your State \/ Err

Returning to our languageobject Parser1 extends RegexParsers {

def pL1: Parser[List[Int]] = rep(pExpr <~ ";")

def pExpr = pAdd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom

def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}

Allows to convert the Parser[String] to Parser[Int]^^ is an alias of

the map function

Page 29: Transform your State \/ Err

Returning to our languageobject Parser1 extends RegexParsers {

def pL1: Parser[List[Int]] = rep(pExpr <~ ";")

def pExpr = pAdd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom

def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}

Allows to convert the Parser[String] to Parser[Int]

String => Int

^^ is an alias of the map function

Page 30: Transform your State \/ Err

Returning to our languageobject Parser1 extends RegexParsers {

def pL1: Parser[List[Int]] = rep(pExpr <~ ";")

def pExpr = pAdd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom

def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}

Parse two inputs in sequence discarding the input of the left or the right respectively

Page 31: Transform your State \/ Err

Returning to our languageobject Parser1 extends RegexParsers {

def pL1: Parser[List[Int]] = rep(pExpr <~ ";")

def pExpr = pAdd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom

def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}

Pattern matching anonymous function

Page 32: Transform your State \/ Err

Returning to our languageobject Parser1 extends RegexParsers {

def pL1: Parser[List[Int]] = rep(pExpr <~ ";")

def pExpr = pAdd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom

def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}

Pattern matching anonymous function

Allows to deconstruct the result of this parser

Page 33: Transform your State \/ Err

Returning to our languageobject Parser1 extends RegexParsers {

def pL1: Parser[List[Int]] = rep(pExpr <~ ";")

def pExpr = pAdd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom

def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}

Pattern matching anonymous function

Allows to deconstruct the result of this parser

Int IntString

“+” discarded Int

Page 34: Transform your State \/ Err

Returning to our languageobject Parser1 extends RegexParsers {

def pL1: Parser[List[Int]] = rep(pExpr <~ ";") def pExpr = pAdd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom

def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}

We want a list of all the expressions we encounter separated by “;”

Page 35: Transform your State \/ Err

Returning to our languageobject Parser1 extends RegexParsers {

def pL1: Parser[List[Int]] = rep(pExpr <~ ";") def pExpr = pAdd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

def pProd: Parser[Int] = pAtom ~ "*" ~ pProd ^^ { case left ~ _ ~ right => left * right } | pAtom

def pAtom: Parser[Int] = """\d+""".r ^^ (_.toInt) | "(" ~> pExpr <~ ")"}

All good?

Page 36: Transform your State \/ Err

Mini testimport Parser1._

parse(pExpr, "1") //> [1.2] parsed: 1parse(pExpr, "2+2") //> [1.4] parsed: 4parse(pExpr, "3*3+4*4*(1+2)") //> [1.14] parsed: 57

parse(pL1, """1; |2+2; |3*3+4*4*(1+2);""".stripMargin) //> [3.15] parsed: List(1, 4, 57)

parse(pL1, "1; 2+2; 3*3+4*4*(1+2);") //> [1.8] parsed: List(1, 4, 57)

Page 37: Transform your State \/ Err

Now …

Page 38: Transform your State \/ Err

Now …

What if we want to add variables?

Page 39: Transform your State \/ Err

What if we want to add variables?

We need to handle two more cases:

Assignation of variablesReferences to variables

Page 40: Transform your State \/ Err

What if we want to add variables?

We need to handle two more cases:

Assignation of variablesReferences to variables

def pRef = pName ^^ { x => get(symtab, x)}

def pVar = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab, name, expr) }

Page 41: Transform your State \/ Err

We need to handle two more cases:

Assignation of variablesReferences to variables

And we need to carry a symbol table ...

def pVar = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab, name, expr) }

What if we want to add variables?

?def pRef = pName ^^ { x => get(symtab, x)}

Page 42: Transform your State \/ Err

Carrying a symbol table

On pRef we need to access the symbol table but leave it unmodified

On pVar we need to update the symbol table

Otherwise we just need to pass it untouched

def pRef = pName ^^ (x => get(symtab, x))

def pVar = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab, name, expr) }

Page 43: Transform your State \/ Err

Enter scalaz.State[S,A]

scalaz has a type that represents state transitions

State[S,A] wraps functions with type:

S => (S, A)Current state

New stateValue computed

Page 44: Transform your State \/ Err

Enter scalaz.State[S,A]

We need to change the type of our parsers ...

Page 45: Transform your State \/ Err

Enter scalaz.State[S,A]

We need to change the type of our parsers ...

Parser[A]

Parser[State[S,A]]

Parser[State[SymTab, A]]

Parser[S[A]]

P[A]

Page 46: Transform your State \/ Err

Enter scalaz.State[S,A]

We need to change the type of our parsers ...We now return A, but in the context of some stateful computationParser[A]

Parser[State[S,A]]

Parser[State[SymTab, A]]

Parser[S[A]]

P[A]

Page 47: Transform your State \/ Err

Enter scalaz.State[S,A]

We need to change the type of our parsers ...We now return A, but in the context of some stateful computation

Our state is the symbol table~Map[String, Int]

Parser[A]

Parser[State[S,A]]

Parser[State[SymTab, A]]

Parser[S[A]]

P[A]

Page 48: Transform your State \/ Err

Parser[A]

Parser[State[S,A]]

Parser[State[SymTab, A]]

Parser[S[A]]

P[A]

Enter scalaz.State[S,A]

We need to change the type of our parsers ...We now return A, but in the context of some stateful computation

type S[A] = State[SymTab,A]

Our state is the symbol table~Map[String, Int]

Page 49: Transform your State \/ Err

Parser[A]

Parser[State[S,A]]

Parser[State[SymTab, A]]

Parser[S[A]]

P[A]

Enter scalaz.State[S,A]

We need to change the type of our parsers ...We now return A, but in the context of some stateful computation

type S[A] = State[SymTab,A]

Our state is the symbol table~Map[String, Int]

type P[A] = Parser[S[A]]

Page 50: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bit

Page 51: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

Page 52: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

P[Int] == Parser[S[Int]]

Page 53: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

P[Int] == Parser[S[Int]]

S[Int]

Page 54: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

P[Int] == Parser[S[Int]]

S[Int]

leftS and rightS areS[Int] and can’t be added directly

Page 55: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

P[Int] == Parser[S[Int]]

S[Int]

But State[S,A] is a Monad[A]

State[SymTab,A]

Page 56: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

P[Int] == Parser[S[Int]]

S[Int]

State[SymTab,A]So we can use a for-comprehension to work with the ints

Page 57: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

P[Int] == Parser[S[Int]]

S[Int]

State[SymTab,A]When we treatState[S,A] as a Monad[A], we work with the values, and the state transitions happens in the background

Page 58: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

P[Int] == Parser[S[Int]]

S[Int]

State[SymTab,A]Here we are chaining three state transitionss0 => (s1, left)s1 => (s2, right)s2 => (s2, left+right)

Page 59: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pAdd: P[Int] = pProd ~ "+" ~ pAdd ^^ { case leftS ~ _ ~ rightS => for { left <- leftS right <- rightS } yield { left + right } } | pProd

def pAdd: Parser[Int] = pProd ~ "+" ~ pAdd ^^ { case left ~ _ ~ right => left + right } | pProd

P[Int] == Parser[S[Int]]

S[Int]

State[SymTab,A]Which can be seen as one big transition:

s0 => ... (s2, left+right)

The State[S,A] we return

Page 60: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pRef: Parser[Int] = pName ^^ { x => get(symtab, x) }

def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }

Page 61: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pRef: Parser[Int] = pName ^^ { x => get(symtab, x) }

def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }

Here we need to access to the symbol table—our state

Page 62: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pRef: Parser[Int] = pName ^^ { x => get(symtab, x) }

def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }

The trick of State.get is to create a new State[S,S] where the state is used as the value

Page 63: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pRef: Parser[Int] = pName ^^ { x => get(symtab, x) }

def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }

The trick of State.get is to create a new State[S,S] where the state is used as the value

Wraps a state transition { s => (s, s) }

Page 64: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pRef: Parser[Int] = pName ^^ { x => get(symtab, x) }

def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }

Wraps a state transition { s => (s, s) }

With such State[S,S] created, now we can use again a for-comprehension to work with the symbol table (the value)

Page 65: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pRef: Parser[Int] = pName ^^ { x => get(symtab, x) }

def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }

Wraps a state transition { s => (s, s) }

We are chaining two state transitions:s => (s, s)s => (s, s(name))

Page 66: Transform your State \/ Err

… and change the parsers themselves a bit

Enter scalaz.State[S,A]

def pRef: Parser[Int] = pName ^^ { x => get(symtab, x) }

def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }

Wraps a state transition { s => (s, s) }

Which can be seen as:

s => ... (s, s(name))

The State[SymTab, Int] == S[Int] we return

Page 67: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }

def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }

Page 68: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }

def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }

The last case

Page 69: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }

def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }

We need to modify the state

Page 70: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }

def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }

We need to modify the state

S[Int]

Page 71: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }

def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }

We need to modify the state

S[SymTab]S[Int]

Page 72: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }

def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }

To change the state we use State.put

S[SymTab]S[Int]

Page 73: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }

Given s, it creates aState[S,Unit] representing this state transition: { _ => (s, ()) }

def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }

S[SymTab]S[Int]

Page 74: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }

This discards any previous state in favor of the one provided, and returns () { _ => (s, ()) }

def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }

S[SymTab]S[Int]

Page 75: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }

def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }

So, the value we get in the for-comprehension is the unit

S[Unit]

S[SymTab]S[Int]

Page 76: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }

def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }

We have four state transitions:s0 => (s1, expr)s1 => (s1, s1)_ => (s2=s1+(name->expr), ())s2 => (s2, ())

S[Unit]

S[SymTab]S[Int]

Page 77: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }

def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }

So we are returning:

s0 => … (s1+(name->expr), ())

State[SymTab, Unit] == S[Unit] S[Unit]

S[SymTab]S[Int]

Page 78: Transform your State \/ Err

Enter scalaz.State[S,A]

… and change the parsers themselves a bitdef pVar: Parser[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ expr => put(symtab,name,expr) }

def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }

Now we know the basics of the State monad

Page 79: Transform your State \/ Err

Running the stateimport Parser2._

parse(pExpr, "1") //> [1.2] parsed: scalaz.package$State$$anon$3@7f560810

Page 80: Transform your State \/ Err

Running the stateimport Parser2._

parse(pExpr, "1") //> [1.2] parsed: scalaz.package$State$$anon$3@7f560810

Our parser now returns State[SymTab,A], not directly a value

Page 81: Transform your State \/ Err

Running the stateimport Parser2._

parse(pExpr, "1") //> [1.2] parsed: scalaz.package$State$$anon$3@7f560810

To compute the final value, we need to provide an initial state and then all the state transitions can be run

Page 82: Transform your State \/ Err

Running the stateimport Parser2._

parse(pExpr, "1").map(state => state.run(Map.empty))//> [1.2] parsed: (Map(),1)

To compute the final value, we need to provide an initial state and then all the state transitions can be run

Page 83: Transform your State \/ Err

Running the stateimport Parser2._

parse(pExpr, "1").map(state => state.run(Map.empty))//> [1.2] parsed: (Map(),1)

To compute the final value, we need to provide an initial state and then all the state transitions can be run

map over ParseResult[S[Int]]

Page 84: Transform your State \/ Err

Running the stateimport Parser2._

parse(pExpr, "1").map(state => state.run(Map.empty))//> [1.2] parsed: (Map(),1)

To compute the final value, we need to provide an initial state and then all the state transitions can be run

map over ParseResult[S[Int]]Initial state

Page 85: Transform your State \/ Err

Running the stateimport Parser2._

parse(pExpr, "1").map(state => state.run(Map.empty))//> [1.2] parsed: (Map(),1)

To compute the final value, we need to provide an initial state and then all the state transitions can be run

map over ParseResult[S[Int]]Initial state

We obtain the final state and the value computed

Page 86: Transform your State \/ Err

Running the stateimport Parser2._

parse(pExpr, "1").map(_.run(Map.empty))//> [1.2] parsed: (Map(),1)

parse(pVar, "var x = 1").map(_.run(Map.empty))//> [1.10] parsed: (Map(x -> 1),())

parse(pExpr, "y * 3").map(_.run(Map("y" -> 2)))//> [1.6] parsed: (Map(y -> 2),6)

Page 87: Transform your State \/ Err

Running the stateimport Parser2._

parse(pExpr, "1").map(_.run(Map.empty))//> [1.2] parsed: (Map(),1)

parse(pVar, "var x = 1").map(_.run(Map.empty))//> [1.10] parsed: (Map(x -> 1),())

parse(pExpr, "y * 3").map(_.run(Map("y" -> 2)))//> [1.6] parsed: (Map(y -> 2),6)

The final state reflects the binding of 1 to x

Page 88: Transform your State \/ Err

Running the stateimport Parser2._

parse(pExpr, "1").map(_.run(Map.empty))//> [1.2] parsed: (Map(),1)

parse(pVar, "var x = 1").map(_.run(Map.empty))//> [1.10] parsed: (Map(x -> 1),())

parse(pExpr, "y * 3").map(_.run(Map("y" -> 2)))//> [1.6] parsed: (Map(y -> 2),6)

The final state reflects the binding of 1 to x

We can provide bindings in the initial state

Page 89: Transform your State \/ Err

Running the stateparse(pL2, """var x = 1; |var y = x * 3; |x + y; |(x + 1) * 3 + 1; |var z = 8;""".stripMargin).map(_.run(Map.empty))//> [5.11] parsed: (Map(x -> 1, y -> 3, z -> 8),List(4, 7))

Page 90: Transform your State \/ Err

Now ...

Page 91: Transform your State \/ Err

Now ...

What if we reference an undeclared variable?parse(pL2, "var x = y;").map(_.run(Map.empty))

Page 92: Transform your State \/ Err

Now ...

What if we reference an undeclared variable?parse(pL2, "var x = y;").map(_.run(Map.empty))

Exception in thread "main" java.util.NoSuchElementException: key not found: y

at scala.collection.MapLike$class.default(MapLike.scala:228)at scala.collection.AbstractMap.default(Map.scala:59)at scala.collection.MapLike$class.apply(MapLike.scala:141)at scala.collection.AbstractMap.apply(Map.scala:59)...

Page 93: Transform your State \/ Err

Handling errors

If our computation can fail, then our types are wrong. We can’t just return A

Page 94: Transform your State \/ Err

Handling errors

If our computation can fail, then our types are wrong. We can’t just return A

We need to handleReferences to undeclared variablesDeclarations of already declared variables

Page 95: Transform your State \/ Err

scalaz.\/

\/[A,B] == A\/B, called “or”, “xor”, “either”, “disjoint union”,..., can be used to handle the cases when a computation can fail

A \/ BThe type of the error. By convention on the left

The type of the value we are computing. By convention on the right

Page 96: Transform your State \/ Err

scalaz.\/

\/[A,B] is the scalaz version ofscala.Either[A,B]

Page 97: Transform your State \/ Err

Handling errors

We will have to change our types again:

type V[A] = Err \/ A

type S[A] = State[SymTab, V[A]]

sealed abstract class Err(override val toString: String)

case class NotDeclared(name: String) extends Err(s"`$name' not declared.")

case class AlreadyDeclared(name: String, value: Int) extends Err(s"`$name' already declared with value `$value'.")

Page 98: Transform your State \/ Err

Bad news

Page 99: Transform your State \/ Err

Bad news

It gets ugly ...

Page 100: Transform your State \/ Err

It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}

Page 101: Transform your State \/ Err

It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}

This is just one parser

Page 102: Transform your State \/ Err

It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}

exprS now contains a \/[Err,Int]

Page 103: Transform your State \/ Err

It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}

I’m using an extra for-comprehension to deal with it

Page 104: Transform your State \/ Err

It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}

Some juggling to construct a left value if the symbol table already has the name

Page 105: Transform your State \/ Err

It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}

If either exprV or this contains a left, all the for-comprehension will be a left, ie, newSymTab will be a left

Page 106: Transform your State \/ Err

It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}

Here I’m “folding” over the\/[Err, SymTab]. If is a left, I need to put that inside the State. If is a right, I need to use the value as the new state

Page 107: Transform your State \/ Err

It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}

-\/ is the extractor of left values, \/- is the extractor of right values

Page 108: Transform your State \/ Err

It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}

State.state allows to put a value inside a State[S,A]. For a given `a’, represents a transition { s => (s, a) }

Page 109: Transform your State \/ Err

It gets ugly ...def pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { exprV <- exprS symtab <- State.get[SymTab] newSymTab = for { expr <- exprV s <- symtab.get(name) match { // if the name exists, creates a left with the error case Some(v) => AlreadyDeclared(name, v).left // if not exists, returns the symtab as a right case None => symtab.right } } yield { s + (name -> expr) // only executes if everything is right } res <- newSymTab match { // if the newSymTab is a left, I need to put the error in the State case -\/(l) => State.state[SymTab, V[Unit]](l.left) // if the newSymTab is a right, I need to use it as the new state, and put the () inside a \/ case \/-(r) => State.put(r).map(_.right) } } yield { res }}

...

Page 110: Transform your State \/ Err

Good news

Page 111: Transform your State \/ Err

Good news

Monad transformers

Page 112: Transform your State \/ Err

Monad transformers

The only thing I know about them, is that they let you combine the effects of two monads, ...

Page 113: Transform your State \/ Err

Monad transformers

The only thing I know about them, is that they let you combine the effects of two monads, ...

… scalaz has a monad transformer, StateT, that let you combine the State monad with any other monad, …

Page 114: Transform your State \/ Err

Monad transformers

The only thing I know about them, is that they let you combine the effects of two monads, ...

… scalaz has a monad transformer, StateT, that let you combine the State monad with any other monad, …

… and this solves all our problems

Page 115: Transform your State \/ Err

Combining State and \/

This are our final type aliases

type SymTab = Map[String, Int]

type V[A] = Err \/ A

type S[A] = StateT[V, SymTab, A]

type P[A] = Parser[S[A]]

Page 116: Transform your State \/ Err

Combining State and \/

This are our final type aliases

type SymTab = Map[String, Int]

type V[A] = Err \/ A

type S[A] = StateT[V, SymTab, A]

type P[A] = Parser[S[A]]

This is the only new one

Page 117: Transform your State \/ Err

Combining State and \/

This are our final type aliases

type SymTab = Map[String, Int]

type V[A] = Err \/ A

type S[A] = StateT[V, SymTab, A]

type P[A] = Parser[S[A]]

StateT is parameterized on the monad with which is combined

Page 118: Transform your State \/ Err

Combining State and \/

This are our final type aliases

type SymTab = Map[String, Int]

type V[A] = Err \/ A

type S[A] = StateT[V, SymTab, A]

type P[A] = Parser[S[A]]

Here is using the monad of\/[A,B]

Page 119: Transform your State \/ Err

Combining State and \/

And one final ingredientval State = StateT.stateTMonadState[SymTab, V]

Page 120: Transform your State \/ Err

Combining State and \/

And one final ingredientval State = StateT.stateTMonadState[SymTab, V]

The companion object of StateT does not have the functions get, put, state, etc.

Page 121: Transform your State \/ Err

Combining State and \/

And one final ingredientval State = StateT.stateTMonadState[SymTab, V]

Instead we have to instantiate one of this “beasts”

Page 122: Transform your State \/ Err

Combining State and \/With that changes, our parser works as before without any modification

Page 123: Transform your State \/ Err

Combining State and \/import Parser4._

parse(pExpr, "1").map(_.run(Map.empty))//> [1.2] parsed: \/-((Map(),1))

parse(pVar, "var x = 1").map(_.run(Map.empty))//> [1.10] parsed: \/-((Map(x -> 1),()))

parse(pExpr, "y * 3").map(_.run(Map("y" -> 2)))//> [1.6] parsed: \/-((Map(y -> 2),6))

Page 124: Transform your State \/ Err

Combining State and \/parse(pL4, """var x = 1; |var y = x * 3; |x + y; |(x + 1) * 3 + 1; |var z = 8;""".stripMargin).map(_.run(Map.empty))//> [5.11] parsed: \/-((Map(x -> 1, y -> 3, z -> 8),List(4, 7)))

Page 125: Transform your State \/ Err

Combining State and \/parse(pL4, """var x = 1; |var y = x * 3; |x + y; |(x + 1) * 3 + 1; |var z = 8;""".stripMargin).map(_.run(Map.empty))//> [5.11] parsed: \/-((Map(x -> 1, y -> 3, z -> 8),List(4, 7)))

The only difference is that now the results are inside a \/, in particular a \/-, ie, a right value, which indicates that everything is OK.

Page 126: Transform your State \/ Err

Handling errors

Everything else is working the same, even our exceptions when an undeclared variable is referenced

Page 127: Transform your State \/ Err

Handling errors

Everything else is working the same, even our exceptions when an undeclared variable is referenced

We must change pRef y pVar to handle the error cases

Page 128: Transform your State \/ Err

Handling errorsdef pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }

def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get vErrInt = symtab.get(name) .toRightDisjunction( NotDeclared(name)) res <- StateT[V, SymTab, Int] { s => vErrInt.map(s -> _) } } yield { res } }

Page 129: Transform your State \/ Err

Handling errorsdef pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }

def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get vErrInt = symtab.get(name) .toRightDisjunction( NotDeclared(name)) res <- StateT[V, SymTab, Int] { s => vErrInt.map(s -> _) } } yield { res } }

We are converting an Option[Int] into \/[Err,Int]

Page 130: Transform your State \/ Err

Handling errorsdef pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }

def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get vErrInt = symtab.get(name) .toRightDisjunction( NotDeclared(name)) res <- StateT[V, SymTab, Int] { s => vErrInt.map(s -> _) } } yield { res } }

We construct our StateT “by hand” by passing the function directly

Page 131: Transform your State \/ Err

Handling errorsdef pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }

def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get vErrInt = symtab.get(name) .toRightDisjunction( NotDeclared(name)) res <- StateT[V, SymTab, Int] { s => vErrInt.map(s -> _) } } yield { res } }

This StateT wraps functions of type SymTab => V[(SymTab,A)] == SymTab => \/[Err, (SymTab,A)]

Page 132: Transform your State \/ Err

Handling errorsdef pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }

def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get vErrInt = symtab.get(name) .toRightDisjunction( NotDeclared(name)) res <- StateT[V, SymTab, Int] { s => vErrInt.map(s -> _) } } yield { res } }

vErrInt is of type\/[Err, Int], we convert it to\/[Err, (SymTab, Int)] with map

Page 133: Transform your State \/ Err

Handling errorsdef pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }

def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get vErrInt = symtab.get(name) .toRightDisjunction( NotDeclared(name)) res <- StateT[V, SymTab, Int] { s => vErrInt.map(s -> _) } } yield { res } }

This state transition will only be made if vErrInt is a right value. If not, all the computation will return the left value

Page 134: Transform your State \/ Err

Handling errorsdef pRef: P[Int] = pName ^^ { name => for { symtab <- State.get } yield { symtab(name) } }

def pRef: P[Int] = pName ^^ { name => for { symtab <- State.get vErrInt = symtab.get(name) .toRightDisjunction( NotDeclared(name)) res <- StateT[V, SymTab, Int] { s => vErrInt.map(s -> _) } } yield { res } }

Here is happening the combination of both monad effects

Page 135: Transform your State \/ Err

Handling errorsdef pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <- symtab.get(name) match { case Some(v) => StateT[V, SymTab, Unit] { s => AlreadyDeclared(name, v).left } case None => State.put(symtab + (name->expr)) } } yield { unit }}

def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } }

Page 136: Transform your State \/ Err

Handling errorsdef pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <- symtab.get(name) match { case Some(v) => StateT[V, SymTab, Unit] { s => AlreadyDeclared(name, v).left } case None => State.put(symtab + (name->expr)) } } yield { unit }}

def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } } We check if the declaration

already exists

Page 137: Transform your State \/ Err

Handling errorsdef pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <- symtab.get(name) match { case Some(v) => StateT[V, SymTab, Unit] { s => AlreadyDeclared(name, v).left } case None => State.put(symtab + (name->expr)) } } yield { unit }}

def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } } If exists, we construct a StateT with

a function returning a left. This stops any further state transitions

Page 138: Transform your State \/ Err

Handling errorsdef pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <- symtab.get(name) match { case Some(v) => StateT[V, SymTab, Unit] { s => AlreadyDeclared(name, v).left } case None => State.put(symtab + (name->expr)) } } yield { unit }}

def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } } If the value not exists, we proceed as

before, updating the state

Page 139: Transform your State \/ Err

Handling errorsdef pVar: P[Unit] = "var" ~ pName ~ "=" ~ pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <- symtab.get(name) match { case Some(v) => StateT[V, SymTab, Unit] { s => AlreadyDeclared(name, v).left } case None => State.put(symtab + (name->expr)) } } yield { unit }}

def pVar: P[Unit] = "var"~pName~"="~pExpr ^^ { case _ ~ name ~ _ ~ exprS => for { expr <- exprS symtab <- State.get unit <-State.put( symtab+(name->expr) ) } yield { unit } } In both cases we are returning

StateT[V, SymTab, Unit]

Page 140: Transform your State \/ Err

Now errors are handled properlyimport Parser4._

parse(pL4, "var x = y;").map(_.run(Map.empty))//> [1.11] parsed: -\/(`y' not declared.)

parse(pL4, "var x = 1; x * x; var x = 2;").map(_.run(Map.empty))//> [1.29] parsed: -\/(`x' already declared with value `1'.)

Page 141: Transform your State \/ Err

Now errors are handled properlyimport Parser4._

parse(pL4, "var x = y;").map(_.run(Map.empty))//> [1.11] parsed: -\/(`y' not declared.)

parse(pL4, "var x = 1; x * x; var x = 2;").map(_.run(Map.empty))//> [1.29] parsed: -\/(`x' already declared with value `1'.)

Both return left values

Page 142: Transform your State \/ Err

github.com/gerferra/parser-stateT

FIN