val pattern = "Sca(la)+"
pattern.matches("Scalalalalala") // true
Atoms: "S", "c", etc.
Combinators: concatentation, bracketing, +
Interpreter: matches
val image = Picture.circle(100).beside(Picture.square(100))
picture.draw()
Atoms: Picture.circle(100), Picture.square(100)
Combinators: beside
Interpreter: draw
val expr = Expr.literal(1) + Expr.literal(2)
expr.eval() // 3
Atoms: Expr.literal(1), Expr.literal(2)
Combinators: +
Interpreter: eval
enum Expr:
case Lit(value: Double)
case Add(l: Expr, r: Expr)
case Sub(l: Expr, r: Expr)
case Mul(l: Expr, r: Expr)
case Div(l: Expr, r: Expr)
def +(that: Expr): Expr = Add(this, that)
def -(that: Expr): Expr = Sub(this, that)
def *(that: Expr): Expr = Mul(this, that)
def /(that: Expr): Expr = Div(this, that)
object Expr:
def literal(value: Double): Expr = Expr.Lit(value)
enum Expr:
case Lit(value: Double)
case Add(l: Expr, r: Expr)
case Sub(l: Expr, r: Expr)
case Mul(l: Expr, r: Expr)
case Div(l: Expr, r: Expr)
def +(that: Expr): Expr = Add(this, that)
def -(that: Expr): Expr = Sub(this, that)
def *(that: Expr): Expr = Mul(this, that)
def /(that: Expr): Expr = Div(this, that)
object Expr:
def literal(value: Double): Expr = Expr.Lit(value)
enum Expr:
// ...
def eval(): Double =
this match
case Lit(value) => value
case Add(l, r) => l.eval() + r.eval()
case Sub(l, r) => l.eval() - r.eval()
case Mul(l, r) => l.eval() * r.eval()
case Div(l, r) => l.eval() / r.eval()
enum Expr:
// ...
def eval(): Double =
this match
case Lit(value) => value
case Add(l, r) => l.eval() + r.eval()
case Sub(l, r) => l.eval() - r.eval()
case Mul(l, r) => l.eval() * r.eval()
case Div(l, r) => l.eval() / r.eval()
enum Expr:
case Lit(value: Double)
case Add(l: Expr, r: Expr)
case Sub(l: Expr, r: Expr)
case Mul(l: Expr, r: Expr)
case Div(l: Expr, r: Expr)
enum StackInstr:
case Lit(value: Double)
case Add
case Sub
case Mul
case Div
enum Expr:
case Lit(value: Double)
case Add(l: Expr, r: Expr)
case Sub(l: Expr, r: Expr)
case Mul(l: Expr, r: Expr)
case Div(l: Expr, r: Expr)
enum StackInstr:
case Lit(value: Double)
case Add
case Sub
case Mul
case Div
enum Expr:
// ...
def compile(): Array[StackInstr] =
this match
case Lit(value) => Array(StackInstr.Lit(value))
case Add(l, r) =>
l.compile() ++ r.compile() ++ Array(StackInstr.Add)
case Sub(l, r) =>
l.compile() ++ r.compile() ++ Array(StackInstr.Sub)
case Mul(l, r) =>
l.compile() ++ r.compile() ++ Array(StackInstr.Mul)
case Div(l, r) =>
l.compile() ++ r.compile() ++ Array(StackInstr.Div)
enum Expr:
// ...
def compile(): Array[StackInstr] =
this match
case Lit(value) => Array(StackInstr.Lit(value))
case Add(l, r) =>
l.compile() ++ r.compile() ++ Array(StackInstr.Add)
case Sub(l, r) =>
l.compile() ++ r.compile() ++ Array(StackInstr.Sub)
case Mul(l, r) =>
l.compile() ++ r.compile() ++ Array(StackInstr.Mul)
case Div(l, r) =>
l.compile() ++ r.compile() ++ Array(StackInstr.Div)
object StackInstr:
def eval(ins: Array[StackInstr]): Double =
val stack: Array[Double]
def loop(sp: Int, ip: Int)
if ip == ins.size then stack(sp - 1)
else ins(ip) match
case Lit(value) =>
stack(sp) = value
loop(sp + 1, ip + 1)
case Add =>
val a = stack(sp - 1)
val b = stack(sp - 2)
stack(sp - 2) = (a + b)
loop(sp - 1, ip + 1)
// ...
object StackInstr:
def eval(ins: Array[StackInstr]): Double =
val stack: Array[Double]
def loop(sp: Int, ip: Int)
if ip == ins.size then stack(sp - 1)
else ins(ip) match
case Lit(value) =>
stack(sp) = value
loop(sp + 1, ip + 1)
case Add =>
val a = stack(sp - 1)
val b = stack(sp - 2)
stack(sp - 2) = (a + b)
loop(sp - 1, ip + 1)
// ...
Tied to low-level implementation techniques. E.g. GCC extensions.
Doesn't generalize
Wide applicability
we can relate one to the other
understanding one helps understand the other
symmetries, opposites, complements
and and ora and b |
true | false |
|---|---|---|
| true | true | false |
| false | false | false |
a or b |
true | false |
|---|---|---|
| true | true | true |
| false | true | false |
case Add(l, r) => l.eval() + r.eval()
Parameters are explicit
Stack is implicit
case Add =>
val a = stack(sp - 1)
val b = stack(sp - 2)
stack(sp - 2) = (a + b)
Parameters are implicit
Stack is explicit
Codata means a function or object
enum Expr:
case Lit(value: Double)
case Add(l: Expr, r: Expr)
case Sub(l: Expr, r: Expr)
case Mul(l: Expr, r: Expr)
case Div(l: Expr, r: Expr)
Data is extensional, defines what it is
trait Op:
def eval: Double
Codata is intensional, defines what it can do
enum StackInstr:
case Lit(value: Double)
case Add
// ...
object StackInstr:
def eval(ins: Array[StackInstr]): Double =
val stack: Array[Double]
def loop(sp: Int, ip: Int)
if ip == ins.size then stack(sp - 1)
else ins(ip) match
case Lit(value) =>
stack(sp) = value
loop(sp + 1, ip + 1)
// ...
enum StackInstr:
case Lit(value: Double)
case Add
// ...
object StackInstr:
def eval(ins: Array[StackInstr]): Double =
val stack: Array[Double]
def loop(sp: Int, ip: Int)
if ip == ins.size then stack(sp - 1)
else ins(ip) match
case Lit(value) =>
stack(sp) = value
loop(sp + 1, ip + 1)
// ...
Data becomes functions
private final var sp: Int = 0
private final var ip: Int = 0
sealed abstract class Op extends Function0[Unit]
final case class Lit(value: Double) extends Op:
def apply(): Unit =
stack(sp) = value
sp = sp + 1
case object Add extends Op:
def apply(): Unit =
val a = stack(sp - 1)
val b = stack(sp - 2)
stack(sp - 2) = (a + b)
sp = sp - 1
// ...
enum StackInstr:
case Lit(value: Double)
case Add
// ...
object StackInstr:
def eval(ins: Array[StackInstr]): Double =
val stack: Array[Double]
def loop(sp: Int, ip: Int)
if ip == ins.size then stack(sp - 1)
else ins(ip) match
case Lit(value) =>
stack(sp) = value
loop(sp + 1, ip + 1)
// ...
Dispatch carries out operation and advances instruction pointer
private final var sp: Int = 0
private final var ip: Int = 0
sealed abstract class Op extends Function0[Unit]
// ...
final def eval(program: Array[Op]): Double =
@tailrec def loop(): Double =
if ip == program.size then stack(sp - 1)
else
val ins = program(ip)
ins()
ip = ip + 1
loop()
loop()
Dispatch calls function and advances instruction pointer
val a = someFunction()
anotherFunction(a)
someFunction((a) => anotherFunction(a))
Another performance improvement
| Direct | CPS | |
|---|---|---|
| Data | AST | Indirect threaded |
| Codata | Subroutine | Direct threaded |
For example, relate FP and OO implementation techniques
@noelwelsh
noel@noelwelsh.com