Topics related to the Expression Problem including but not limited - - PowerPoint PPT Presentation

topics related to the expression problem
SMART_READER_LITE
LIVE PREVIEW

Topics related to the Expression Problem including but not limited - - PowerPoint PPT Presentation

Topics related to the Expression Problem including but not limited to Compositional and Linear Encoding (of Synthesized and Inherited Attributes as Object Algebras in Scala) Tillmann Rendel University of Tbingen, Germany Invited Talk by


slide-1
SLIDE 1

Topics related to the Expression Problem

including but not limited to

Compositional and Linear Encoding (of Synthesized and Inherited Attributes as Object Algebras in Scala)

Tillmann Rendel University of Tübingen, Germany

Invited Talk by Tillmann Rendel at the Workshop on Generic Programming, Vancouver, British Columbia, August 30, 2015

slide-2
SLIDE 2

Overview Map

Expression Problem

2/42

slide-3
SLIDE 3

Expression Problem

3/42

Wadler 1998 (The Expression Problem)

slide-4
SLIDE 4

Expression Problem

tree- shaped data

4/42

slide-5
SLIDE 5

Expression Problem

tree- shaped data

4/42

slide-6
SLIDE 6

Expression Problem

tree- shaped data

4/42

slide-7
SLIDE 7

Expression Problem

constructors tree- shaped data

4/42

slide-8
SLIDE 8

Expression Problem

constructors consumer tree- shaped data

4/42

slide-9
SLIDE 9

Expression Problem

constructors consumer tree- shaped data

4/42

slide-10
SLIDE 10

Expression Problem

constructors consumer tree- shaped data

4/42

slide-11
SLIDE 11

Expression Problem

constructors consumer tree- shaped data function function

4/42

slide-12
SLIDE 12

Expression Problem

constructors consumer tree- shaped data function function function

4/42

slide-13
SLIDE 13

Expression Problem

constructors consumer tree- shaped data

4/42

slide-14
SLIDE 14

Expression Problem

constructors consumer tree- shaped data class class class

4/42

slide-15
SLIDE 15

Expression Problem

constructors consumer tree- shaped data class class class class

4/42

slide-16
SLIDE 16

Expression Problem

constructors consumer tree- shaped data

4/42

slide-17
SLIDE 17

Expression Problem

constructors consumer tree- shaped data

4/42

slide-18
SLIDE 18

Deep, Shallow and Final* Embedding

*also known as „polymorphic embedding“ or „object algebras“

5/42

slide-19
SLIDE 19

Deep, Shallow and Final* Embedding

*also known as „polymorphic embedding“ or „object algebras“

5/42

Carette, Kiselyov, Shan 2007, 2009 (Finally Tagless Emb. ...) Hofer, Rendel, Ostermann, De Moors 2008 (Polymorphic Emb. …) Oliveira, Cook 2012 (Extensibility for the Masses: ...)

slide-20
SLIDE 20

Extensible Records

Some languages with basic or no support for extensible algebraic data types still support extensible and very expressive record types.

  • Haskell: type classes, multiple constraints
  • Scala: traits, mixin composition
  • Java: interfaces, multiple upper bounds

Many solutions to the expression problem represent variants as records to benefit from advanced record support.

6/42

slide-21
SLIDE 21

Deep Embedding

trait Exp case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp def eval(e: Exp): Int = e match { case Lit(n) => n case Add(l, r) => eval(l) + eval(r) }

7/42

slide-22
SLIDE 22

Deep Embedding

trait Exp case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp def eval(e: Exp): Int = e match { case Lit(n) => n case Add(l, r) => eval(l) + eval(r) }

7/42

slide-23
SLIDE 23

Deep Embedding

trait Exp case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp def eval(e: Exp): Int = e match { case Lit(n) => n case Add(l, r) => eval(l) + eval(r) }

7/42

slide-24
SLIDE 24

Deep Embedding

trait Exp case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp def eval(e: Exp): Int = e match { case Lit(n) => n case Add(l, r) => eval(l) + eval(r) }

7/42

slide-25
SLIDE 25

Deep Embedding

trait Exp case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp def eval(e: Exp): Int = e match { case Lit(n) => n case Add(l, r) => eval(l) + eval(r) }

7/42

slide-26
SLIDE 26

Deep Embedding

trait Exp case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp def eval(e: Exp): Int = e match { case Lit(n) => n case Add(l, r) => eval(l) + eval(r) }

7/42

slide-27
SLIDE 27

Deep Embedding

trait Exp case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp def eval(e: Exp): Int = e match { case Lit(n) => n case Add(l, r) => eval(l) + eval(r) }

7/42

slide-28
SLIDE 28

Deep Embedding

trait Exp case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp def eval(e: Exp): Int = e match { case Lit(n) => n case Add(l, r) => eval(l) + eval(r) }

7/42

slide-29
SLIDE 29

Deep Embedding

trait Exp case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp def eval(e: Exp): Int = e match { case Lit(n) => n case Add(l, r) => eval(l) + eval(r) }

7/42

slide-30
SLIDE 30

trait Exp case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp def eval(e: Exp): Int = e match { case Lit(n) => n case Add(l, r) => eval(l) + eval(r) }

8/42

slide-31
SLIDE 31

trait Exp { def eval: Int } case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp case Lit(n) => n case Add(l, r) => eval(l) + eval(r)

8/42

slide-32
SLIDE 32

trait Exp { def eval: Int } case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp case Lit(n) => n case Add(l, r) => eval(l) + eval(r)

8/42

slide-33
SLIDE 33

trait Exp { def eval: Int } def Lit(n: Int) = new Exp { def eval = n } case class Add(l: Exp, r: Exp) extends Exp case Add(l, r) => eval(l) + eval(r)

8/42

slide-34
SLIDE 34

trait Exp { def eval: Int } def Lit(n: Int) = new Exp { def eval = n } case class Add(l: Exp, r: Exp) extends Exp case Add(l, r) => eval(l) + eval(r)

8/42

slide-35
SLIDE 35

trait Exp { def eval: Int } def Lit(n: Int) = new Exp { def eval = n } def Add(l: Exp, r: Exp) = new Exp { def eval = l.eval + r.eval }

8/42

slide-36
SLIDE 36

Shallow Embedding

trait Exp { def eval: Int } def Lit(n: Int) = new Exp { def eval = n } def Add(l: Exp, r: Exp) = new Exp { def eval = l.eval + r.eval }

8/42

slide-37
SLIDE 37

Deep Embedding Shallow Embedding

(Re|De)functionalization

9/42 Refunctionalize Defunctionalize

Reynolds 1972 (Definitional Interpreters for Higher-Order Languages) Danvy and Milliken 2007 (Refunctionalization at Work)

slide-38
SLIDE 38

Deep Embedding Shallow Embedding Folding Final Embedding

(Re|De)functionalization

9/42 Refunctionalize Defunctionalize Refunctionalize Defunctionalize

slide-39
SLIDE 39

trait Alg[T] { def Lit(n: Int): T def Add(l: T, r: T): T } trait Exp case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp def fold[T](e: Exp, alg: Alg[T]): T = e match { case Lit(n) => alg.Lit(n) case Add(l, r) => alg.Add(fold(l, alg), fold(r, alg)) }

Folding

10/42

slide-40
SLIDE 40

trait Alg[T] { def Lit(n: Int): T def Add(l: T, r: T): T } trait Exp case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp def fold[T](e: Exp, alg: Alg[T]): T = e match { case Lit(n) => alg.Lit(n) case Add(l, r) => alg.Add(fold(l, alg), fold(r, alg)) }

Folding

10/42

slide-41
SLIDE 41

trait Alg[T] { def Lit(n: Int): T def Add(l: T, r: T): T } trait Exp case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp def fold[T](e: Exp, alg: Alg[T]): T = e match { case Lit(n) => alg.Lit(n) case Add(l, r) => alg.Add(fold(l, alg), fold(r, alg)) }

Folding

10/42

slide-42
SLIDE 42

trait Alg[T] { def Lit(n: Int): T def Add(l: T, r: T): T } trait Exp case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp def fold[T](e: Exp, alg: Alg[T]): T = e match { case Lit(n) => alg.Lit(n) case Add(l, r) => alg.Add(fold(l, alg), fold(r, alg)) }

Folding

10/42

slide-43
SLIDE 43

trait Alg[T] { def Lit(n: Int): T def Add(l: T, r: T): T } trait Exp case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp def fold[T](e: Exp, alg: Alg[T]): T = e match { case Lit(n) => alg.Lit(n) case Add(l, r) => alg.Add(fold(l, alg), fold(r, alg)) }

11/42

slide-44
SLIDE 44

trait Alg[T] { def Lit(n: Int): T def Add(l: T, r: T): T } trait Exp { def fold[T](alg: Alg[T]): T } case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp case Lit(n) => alg.Lit(n) case Add(l, r) => alg.Add(fold(l, alg), fold(r, alg))

11/42

slide-45
SLIDE 45

trait Alg[T] { def Lit(n: Int): T def Add(l: T, r: T): T } trait Exp { def fold[T](alg: Alg[T]): T } case class Lit(n: Int) extends Exp case class Add(l: Exp, r: Exp) extends Exp case Lit(n) => alg.Lit(n) case Add(l, r) => alg.Add(fold(l, alg), fold(r, alg))

11/42

slide-46
SLIDE 46

trait Alg[T] { def Lit(n: Int): T def Add(l: T, r: T): T } trait Exp { def fold[T](alg: Alg[T]): T } def Lit(n: Int) = new Exp { def fold[T](alg: Alg[T]) = alg.lit(n) } case class Add(l: Exp, r: Exp) extends Exp case Add(l, r) => alg.Add(fold(l, alg), fold(r, alg))

11/42

slide-47
SLIDE 47

trait Alg[T] { def Lit(n: Int): T def Add(l: T, r: T): T } trait Exp { def fold[T](alg: Alg[T]): T } def Lit(n: Int) = new Exp { def fold[T](alg: Alg[T]) = alg.lit(n) } case class Add(l: Exp, r: Exp) extends Exp case Add(l, r) => alg.Add(fold(l, alg), fold(r, alg))

11/42

slide-48
SLIDE 48

trait Alg[T] { def Lit(n: Int): T def Add(l: T, r: T): T } trait Exp { def fold[T](alg: Alg[T]): T } def Lit(n: Int) = new Exp { def fold[T](alg: Alg[T]) = alg.lit(n) } def Add(l: Exp, r: Exp) = new Exp { def fold[T](alg: Alg[T]) = alg.Add(l.fold(alg), r.fold(alg)) }

11/42

slide-49
SLIDE 49

Final Embedding

trait Alg[T] { def Lit(n: Int): T def Add(l: T, r: T): T } trait Exp { def fold[T](alg: Alg[T]): T } def Lit(n: Int) = new Exp { def fold[T](alg: Alg[T]) = alg.lit(n) } def Add(l: Exp, r: Exp) = new Exp { def fold[T](alg: Alg[T]) = alg.Add(l.fold(alg), r.fold(alg)) }

11/42

slide-50
SLIDE 50

To study the relationship

  • f embedding approaches,

we should study defunctionalization and refunctionalization

12/42

slide-51
SLIDE 51

To study the relationship

  • f embedding approaches,

we should study defunctionalization and refunctionalization (for Scala-ish languages?!)

12/42

slide-52
SLIDE 52

Modular, Scalable, and Compositional Encoding (of Attribute Grammars in Scala)

13/42

Rendel, Brachthäuser, Ostermann 2014 (From Object Algebras to ...)

slide-53
SLIDE 53

constructors consumer

Two Dimensions of Modularity

14/42

slide-54
SLIDE 54

Third Dimension of Modularity

To scale to large programs, solutions to the expression problem need to support components with inner structure:

  • Consumers that depend on other consumers.
  • Consumers that depend on context information.
  • Consumers that are fused together.

15/42

slide-55
SLIDE 55

Bottom-Up Data Flow

16/42

slide-56
SLIDE 56

Synthesized Attributes

Grammar

e0 → n { Lit } e1 → e2 "+" e3 { Add }

Signature

trait Sig[E] { def Lit: Int ⇒E def Add: (E, E) ⇒ E }

Equations

e0.value = n e1 .value = e2.value + e3.value

Algebra

val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 } 17/42

slide-57
SLIDE 57

Synthesized Attributes

Grammar

e0 → n { Lit } e1 → e2 "+" e3 { Add }

Signature

trait Sig[E] { def Lit: Int ⇒E def Add: (E, E) ⇒ E }

Equations

e0.value = n e1 .value = e2.value + e3.value

Algebra

val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 } 17/42

slide-58
SLIDE 58

Synthesized Attributes

Grammar

e0 → n { Lit } e1 → e2 "+" e3 { Add }

Signature

trait Sig[E] { def Lit: Int ⇒E def Add: (E, E) ⇒ E }

Equations

e0.value = n e1 .value = e2.value + e3.value

Algebra

val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 } 17/42

slide-59
SLIDE 59

Synthesized Attributes

Grammar

e0 → n { Lit } e1 → e2 "+" e3 { Add }

Signature

trait Sig[E] { def Lit: Int ⇒E def Add: (E, E) ⇒ E }

Equations

e0.value = n e1 .value = e2.value + e3.value

Algebra

val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 } 17/42

slide-60
SLIDE 60

Synthesized Attributes

Grammar

e0 → n { Lit } e1 → e2 "+" e3 { Add }

Signature

trait Sig[E] { def Lit: Int ⇒E def Add: (E, E) ⇒ E }

Equations

e0.value = n e1 .value = e2.value + e3.value

Algebra

val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 } 17/42

slide-61
SLIDE 61

Synthesized Attributes

Grammar

e0 → n { Lit } e1 → e2 "+" e3 { Add }

Signature

trait Sig[E] { def Lit: Int ⇒E def Add: (E, E) ⇒ E }

Equations

e0.value = n e1 .value = e2.value + e3.value

Algebra

val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 } 17/42

slide-62
SLIDE 62

Synthesized Attributes

Grammar

e0 → n { Lit } e1 → e2 "+" e3 { Add }

Signature

trait Sig[E] { def Lit: Int ⇒E def Add: (E, E) ⇒ E }

Equations

e0.value = n e1 .value = e2.value + e3.value

Algebra

val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 } 17/42

slide-63
SLIDE 63

Synthesized Attributes

Grammar

e0 → n { Lit } e1 → e2 "+" e3 { Add }

Signature

trait Sig[E] { def Lit: Int ⇒E def Add: (E, E) ⇒ E }

Equations

e0.value = n e1 .value = e2.value + e3.value

Algebra

val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 } 17/42

slide-64
SLIDE 64

Synthesized Attributes

Grammar

e0 → n { Lit } e1 → e2 "+" e3 { Add }

Signature

trait Sig[E] { def Lit: Int ⇒E def Add: (E, E) ⇒ E }

Equations

e0.value = n e1 .value = e2.value + e3.value

Algebra

val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 } 17/42

slide-65
SLIDE 65

Synthesized Attributes

Grammar

e0 → n { Lit } e1 → e2 "+" e3 { Add }

Signature

trait Sig[E] { def Lit: Int ⇒E def Add: (E, E) ⇒ E }

Equations

e0.value = n e1 .value = e2.value + e3.value

Algebra

val Alg = new Sig[Int] { def Lit = n ⇒ n def Add = (e2, e3) ⇒ e2 + e3 } 17/42

slide-66
SLIDE 66

Top-Down Data Flow

18/42

slide-67
SLIDE 67

Inherited Attributes

Grammar

e0 → n { Lit } e1 → e2 "+" e3 { Add }

Signature

trait Sig[E] { def Add1: E ⇒E def Add2: (E, E) ⇒ E }

Equations

e2 .left = true

e3.left = false

Algebra

val Alg = new Sig[Bool] { def Add1 = e ⇒ true def Add2 = (e1, e2) ⇒ false } 19/42

slide-68
SLIDE 68

Inherited Attributes

Grammar

e0 → n { Lit } e1 → e2 "+" e3 { Add }

Signature

trait Sig[E] { def Add1: E ⇒E def Add2: (E, E) ⇒ E }

Equations

e2 .left = true

e3.left = false

Algebra

val Alg = new Sig[Bool] { def Add1 = e ⇒ true def Add2 = (e1, e2) ⇒ false } 19/42

slide-69
SLIDE 69

Inherited Attributes

Grammar

e0 → n { Lit } e1 → e2 "+" e3 { Add }

Signature

trait Sig[E] { def Add1: E ⇒E def Add2: (E, E) ⇒ E }

Equations

e2 .left = true

e3.left = false

Algebra

val Alg = new Sig[Bool] { def Add1 = e ⇒ true def Add2 = (e1, e2) ⇒ false } 19/42

slide-70
SLIDE 70

Inherited Attributes

Grammar

e0 → n { Lit } e1 → e2 "+" e3 { Add }

Signature

trait Sig[E] { def Add1: E ⇒E def Add2: (E, E) ⇒ E }

Equations

e2 .left = true

e3.left = false

Algebra

val Alg = new Sig[Bool] { def Add1 = e ⇒ true def Add2 = (e1, e2) ⇒ false } 19/42

slide-71
SLIDE 71

Inherited Attributes

Grammar

e0 → n { Lit } e1 → e2 "+" e3 { Add }

Signature

trait Sig[E] { def Add1: E ⇒E def Add2: (E, E) ⇒ E }

Equations

e2 .left = true

e3.left = false

Algebra

val Alg = new Sig[Bool] { def Add1 = e ⇒ true def Add2 = (e1, e2) ⇒ false } 19/42

slide-72
SLIDE 72

Inherited Attributes

Grammar

e0 → n { Lit } e1 → e2 "+" e3 { Add }

Signature

trait Sig[E] { def Add1: E ⇒E def Add2: (E, E) ⇒ E }

Equations

e2 .left = true

e3.left = false

Algebra

val Alg = new Sig[Bool] { def Add1 = e ⇒ true def Add2 = (e1, e2) ⇒ false } 19/42

slide-73
SLIDE 73

Composition

20/42

slide-74
SLIDE 74

Composition

compose

20/42

slide-75
SLIDE 75

Composition

compose

20/42

slide-76
SLIDE 76

Composition

assemble

20/42

slide-77
SLIDE 77

compose( , ) =

Extensible Records

trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B](implicit m: Mix[A, B]): (A, B) ⇒ A with B

Implicit Macros

implicit def mixAll[A, B]: Mix[A, B] = macro impl[A, B] def impl[A, B](c: Context)(implicit aT: c.WeakTypeTag[A], bT: c.WeakTypeTag[B]): c.Expr[Mix[A, B]] = { ... 21/42

slide-78
SLIDE 78

compose( , ) =

Extensible Records

trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B](implicit m: Mix[A, B]): (A, B) ⇒ A with B

Implicit Macros

implicit def mixAll[A, B]: Mix[A, B] = macro impl[A, B] def impl[A, B](c: Context)(implicit aT: c.WeakTypeTag[A], bT: c.WeakTypeTag[B]): c.Expr[Mix[A, B]] = { ... 21/42

slide-79
SLIDE 79

compose( , ) =

Extensible Records

trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B](implicit m: Mix[A, B]): (A, B) ⇒ A with B

Implicit Macros

implicit def mixAll[A, B]: Mix[A, B] = macro impl[A, B] def impl[A, B](c: Context)(implicit aT: c.WeakTypeTag[A], bT: c.WeakTypeTag[B]): c.Expr[Mix[A, B]] = { ... 21/42

slide-80
SLIDE 80

compose( , ) =

Extensible Records

trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B](implicit m: Mix[A, B]): (A, B) ⇒ A with B

Implicit Macros

implicit def mixAll[A, B]: Mix[A, B] = macro impl[A, B] def impl[A, B](c: Context)(implicit aT: c.WeakTypeTag[A], bT: c.WeakTypeTag[B]): c.Expr[Mix[A, B]] = { ... 21/42

slide-81
SLIDE 81

compose( , ) =

Extensible Records

trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B](implicit m: Mix[A, B]): (A, B) ⇒ A with B

Implicit Macros

implicit def mixAll[A, B]: Mix[A, B] = macro impl[A, B] def impl[A, B](c: Context)(implicit aT: c.WeakTypeTag[A], bT: c.WeakTypeTag[B]): c.Expr[Mix[A, B]] = { ... 21/42

slide-82
SLIDE 82

compose( , ) =

Extensible Records

trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B](implicit m: Mix[A, B]): (A, B) ⇒ A with B

Implicit Macros

implicit def mixAll[A, B]: Mix[A, B] = macro impl[A, B] def impl[A, B](c: Context)(implicit aT: c.WeakTypeTag[A], bT: c.WeakTypeTag[B]): c.Expr[Mix[A, B]] = { ... 21/42

slide-83
SLIDE 83

compose( , ) =

Extensible Records

trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B](implicit m: Mix[A, B]): (A, B) ⇒ A with B

Implicit Macros

implicit def mixAll[A, B]: Mix[A, B] = macro impl[A, B] def impl[A, B](c: Context)(implicit aT: c.WeakTypeTag[A], bT: c.WeakTypeTag[B]): c.Expr[Mix[A, B]] = { ... 21/42

slide-84
SLIDE 84

compose( , ) =

Extensible Records

trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B](implicit m: Mix[A, B]): (A, B) ⇒ A with B

Implicit Macros

implicit def mixAll[A, B]: Mix[A, B] = macro impl[A, B] def impl[A, B](c: Context)(implicit aT: c.WeakTypeTag[A], bT: c.WeakTypeTag[B]): c.Expr[Mix[A, B]] = { ... 21/42

slide-85
SLIDE 85

compose( , ) =

Extensible Records

trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B](implicit m: Mix[A, B]): (A, B) ⇒ A with B

Implicit Macros

implicit def mixAll[A, B]: Mix[A, B] = macro impl[A, B] def impl[A, B](c: Context)(implicit aT: c.WeakTypeTag[A], bT: c.WeakTypeTag[B]): c.Expr[Mix[A, B]] = { ... 21/42

slide-86
SLIDE 86

compose( , ) =

Extensible Records

trait HasValue { def value: Int } trait HasLeft { def left: Bool } def mix[A, B](implicit m: Mix[A, B]): (A, B) ⇒ A with B

Implicit Macros

implicit def mixAll[A, B]: Mix[A, B] = macro impl[A, B] def impl[A, B](c: Context)(implicit aT: c.WeakTypeTag[A], bT: c.WeakTypeTag[B]): c.Expr[Mix[A, B]] = { ... 21/42

slide-87
SLIDE 87

compose( , ) =

Dependency Tracking

trait Sig[-E, -C, +O] { def Lit: Int ⇒C ⇒ O def Add: (E, E) ⇒ C ⇒ O } 22/42

slide-88
SLIDE 88

compose( , ) =

Dependency Tracking

trait Sig[-E, -C, +O] { def Lit: Int ⇒C ⇒ O def Add: (E, E) ⇒ C ⇒ O } 22/42

slide-89
SLIDE 89

compose( , ) =

Dependency Tracking

trait Sig[-E, -C, +O] { def Lit: Int ⇒C ⇒ O def Add: (E, E) ⇒ C ⇒ O }

current node

22/42

slide-90
SLIDE 90

compose( , ) =

Dependency Tracking

trait Sig[-E, -C, +O] { def Lit: Int ⇒C ⇒ O def Add: (E, E) ⇒ C ⇒ O }

current node children

22/42

slide-91
SLIDE 91

compose( , ) =

Dependency Tracking

trait Sig[-E, -C, +O] { def Lit: Int ⇒C ⇒ O def Add: (E, E) ⇒ C ⇒ O }

current node context from parent

22/42

slide-92
SLIDE 92

compose( , ) =

Dependency Tracking

trait Sig[-E, -C, +O] { def Lit: Int ⇒C ⇒ O def Add: (E, E) ⇒ C ⇒ O }

current node

22/42

slide-93
SLIDE 93

compose( , ) =

Dependency Tracking

trait Sig[-E, -C, +O] { def Lit: Int ⇒C ⇒ O def Add: (E, E) ⇒ C ⇒ O } 22/42

slide-94
SLIDE 94

compose( , ) =

Dependency Tracking

trait Sig[-E, -C, +O] { def Lit: Int ⇒C ⇒ O def Add: (E, E) ⇒ C ⇒ O } 22/42

slide-95
SLIDE 95

compose( , ) =

Composing two algebras

def compose [E1, C1, O1, E2, C2 >: C1 with O1, O2] (alg1: Sig[E1, C1, O1], alg2: Sig[E2, C2, O2]): Sig[E1 with E2, C1, O1 with O2] 23/42

slide-96
SLIDE 96

compose( , ) =

Composing two algebras

def compose [E1, C1, O1, E2, C2 >: C1 with O1, O2] (alg1: Sig[E1, C1, O1], alg2: Sig[E2, C2, O2]): Sig[E1 with E2, C1, O1 with O2] 23/42

slide-97
SLIDE 97

compose( , ) =

Composing two algebras

def compose [E1, C1, O1, E2, C2 >: C1 with O1, O2] (alg1: Sig[E1, C1, O1], alg2: Sig[E2, C2, O2]): Sig[E1 with E2, C1, O1 with O2] 23/42

slide-98
SLIDE 98

compose( , ) =

Composing two algebras

def compose [E1, C1, O1, E2, C2 >: C1 with O1, O2] (alg1: Sig[E1, C1, O1], alg2: Sig[E2, C2, O2]): Sig[E1 with E2, C1, O1 with O2] 23/42

slide-99
SLIDE 99

compose( , ) =

Composing two algebras

def compose [E1, C1, O1, E2, C2 >: C1 with O1, O2] (alg1: Sig[E1, C1, O1], alg2: Sig[E2, C2, O2]): Sig[E1 with E2, C1, O1 with O2] 23/42

slide-100
SLIDE 100

compose( , ) =

Composing two algebras

def compose [E1, C1, O1, E2, C2 >: C1 with O1, O2] (alg1: Sig[E1, C1, O1], alg2: Sig[E2, C2, O2]): Sig[E1 with E2, C1, O1 with O2] 23/42

slide-101
SLIDE 101

compose( , ) =

Composing two algebras

def compose [E1, C1, O1, E2, C2 >: C1 with O1, O2] (alg1: Sig[E1, C1, O1], alg2: Sig[E2, C2, O2]): Sig[E1 with E2, C1, O1 with O2] 23/42

slide-102
SLIDE 102

compose( , ) =

Composing two algebras

def compose [E1, C1, O1, E2, C2 >: C1 with O1, O2] (alg1: Sig[E1, C1, O1], alg2: Sig[E2, C2, O2]): Sig[E1 with E2, C1, O1 with O2] 23/42

slide-103
SLIDE 103

Assembling a one-pass traversal

def assemble [C, O] (alg1: Sig1[C with O, C, O], alg2: Sig2[C with O, C, C]): Sig[C ⇒ C with O]

assemble( , ) =

24/42

slide-104
SLIDE 104

Assembling a one-pass traversal

def assemble [C, O] (alg1: Sig1[C with O, C, O], alg2: Sig2[C with O, C, C]): Sig[C ⇒ C with O]

assemble( , ) =

24/42

slide-105
SLIDE 105

Assembling a one-pass traversal

def assemble [C, O] (alg1: Sig1[C with O, C, O], alg2: Sig2[C with O, C, C]): Sig[C ⇒ C with O]

assemble( , ) =

24/42

slide-106
SLIDE 106

Assembling a one-pass traversal

def assemble [C, O] (alg1: Sig1[C with O, C, O], alg2: Sig2[C with O, C, C]): Sig[C ⇒ C with O]

assemble( , ) =

24/42

slide-107
SLIDE 107

Assembling a one-pass traversal

def assemble [C, O] (alg1: Sig1[C with O, C, O], alg2: Sig2[C with O, C, C]): Sig[C ⇒ C with O]

assemble( , ) =

24/42

slide-108
SLIDE 108

Results

Object algebras correspond to synthesized attributes (bottom-up data-flow) We extend object algebras to support inherited attributes (top-down data flow) We assemble multiple algebras to support L-attributed grammars (arbitrary one-pass compiler)

25/42

slide-109
SLIDE 109

Results

Object algebras correspond to synthesized attributes (bottom-up data-flow) We extend object algebras to support inherited attributes (top-down data flow) We assemble multiple algebras to support L-attributed grammars (arbitrary one-pass compiler)

25/42

slide-110
SLIDE 110

Results

Object algebras correspond to synthesized attributes (bottom-up data-flow) We extend object algebras to support inherited attributes (top-down data flow) We assemble multiple algebras to support L-attributed grammars (arbitrary one-pass compiler)

25/42

slide-111
SLIDE 111

25/42

Results

Object algebras correspond to synthesized attributes (bottom-up data-flow) We extend object algebras to support inherited attributes (top-down data flow) We assemble multiple algebras to support L-attributed grammars (arbitrary one-pass compiler)

slide-112
SLIDE 112

Modularizing a One-Pass Compiler

  • existing one-pass compiler for a subset of C
  • 9 nonterminals
  • written for teaching at Aarhus university

(not by the authors)

26/42

slide-113
SLIDE 113

Monolithic compiler

1 file 807 lines of Java code entangled

Modularized compiler

  • ca. 25 files

1620 lines of Scala code modular

27/42

slide-114
SLIDE 114

Properties of the Encoding

Modular Attributes are defined and type-checked separately Scalable Scala code size is linear in AG specification size. Compositional Each AG artifact is represented as a Scala value.

28/42

slide-115
SLIDE 115

Properties of the Encoding

Modular Attributes are defined and type-checked separately Scalable Scala code size is linear in AG specification size. Compositional Each AG artifact is represented as a Scala value.

28/42

slide-116
SLIDE 116

Properties of the Encoding

Modular Attributes are defined and type-checked separately Scalable Scala code size is linear in AG specification size. Compositional Each AG artifact is represented as a Scala value.

28/42

slide-117
SLIDE 117

Properties of the Encoding

Modular Attributes are defined and type-checked separately Scalable Scala code size is linear in AG specification size Compositional Each AG artifact is represented as a Scala value.

28/42

slide-118
SLIDE 118

Scala Experience

Good

  • Traits & static mixin

composition

  • Type Inference

(when it works)

  • Implicits
  • Existence of Macros
  • Implicit Macros

Bad

  • Missing introduction

for intersection types

  • Type Inference

(when it fails)

  • Details of macro

programming

29/42

slide-119
SLIDE 119

Some OO-inspired features of Scala are very good for modular, scalable and compositional encoding, but we don't need full OO.

30/42

slide-120
SLIDE 120

The Co-Expression Problem

31/42

based on work in progress with Brachthäuser, Giarrusso and Ostermann

slide-121
SLIDE 121

Duality

A data type is defined by constructors. Functions consume data values by covering all constructors. How to extend constructors and consumers?

32/42

slide-122
SLIDE 122

Duality

A data type is defined by constructors. Functions consume data values by covering all constructors. How to extend constructors and consumers? A codata type is defined by destructors.

32/42

slide-123
SLIDE 123

Duality

A data type is defined by constructors. Functions consume data values by covering all constructors. How to extend constructors and consumers? A codata type is defined by destructors. Functions generate codata values by covering all destructors.

32/42

slide-124
SLIDE 124

Duality

A data type is defined by constructors. Functions consume data values by covering all constructors. How to extend constructors and consumers? A codata type is defined by destructors. Functions generate codata values by covering all destructors. How to extend destructors and generators?

32/42

slide-125
SLIDE 125

Impact

  • Where are real instances of the co-expression

problem?

  • Can (should?) the co-expression problem guide

research on codata representation like the expression problem guided research on data representation?

33/42

slide-126
SLIDE 126

Solutions

  • Which solutions of the expression problem also

solve the co-expression problem?

  • Which solutions of the expression problem are

dualizable to solutions of the co-expression problem?

  • Are there solutions that are unique to one of the

problems, maybe pointing to asymetric support for data and codata in programming languages?

34/42

slide-127
SLIDE 127

Expression Lemma

  • Seems to formalize the semantic aspects of a

combination of the usual expression problem and the co-expression problem.

  • In what setting should we study the syntactic and

engineering aspects?

35/42

Lämmel, Rypacek 2008 (The Expression Lemma)

slide-128
SLIDE 128

To investigate the relationship of expression problem and coexpression problem we should use a language with symmetric support for data and codata types.

36/42

slide-129
SLIDE 129

A Very Simple Language with Symmetric Support for Data and Codata

37/42

Rendel, Trieflinger, Ostermann 2015 (Automatic Refunctionalization ...)

slide-130
SLIDE 130

Symmetric Support

data Nat where zero() : Nat succ(Nat) : Nat fun add(Nat, Nat) were add(zero(), n) = n add(succ(m), n) = succ(add(m, n)) codata Stream where Stream.head() : Nat Stream.tail() : Stream fun count(Nat) where count(n).head() = n count(n).tail() = count(succ(n))

38/42

slide-131
SLIDE 131

Symmetric Support

data Nat where zero() : Nat succ(Nat) : Nat fun add(Nat, Nat) were add(zero(), n) = n add(succ(m), n) = succ(add(m, n)) codata Stream where Stream.head() : Nat Stream.tail() : Stream fun count(Nat) where count(n).head() = n count(n).tail() = count(succ(n))

38/42

slide-132
SLIDE 132

Symmetric Support

data Nat where zero() : Nat succ(Nat) : Nat fun add(Nat, Nat) were add(zero(), n) = n add(succ(m), n) = succ(add(m, n)) codata Stream where Stream.head() : Nat Stream.tail() : Stream fun count(Nat) where count(n).head() = n count(n).tail() = count(succ(n))

38/42

slide-133
SLIDE 133

Symmetric Support

data Nat where zero() : Nat succ(Nat) : Nat fun add(Nat, Nat) were add(zero(), n) = n add(succ(m), n) = succ(add(m, n)) codata Stream where Stream.head() : Nat Stream.tail() : Stream fun count(Nat) where count(n).head() = n count(n).tail() = count(succ(n))

38/42

slide-134
SLIDE 134

Symmetric Support

data Nat where zero() : Nat succ(Nat) : Nat fun add(Nat, Nat) were add(zero(), n) = n add(succ(m), n) = succ(add(m, n)) codata Stream where Stream.head() : Nat Stream.tail() : Stream fun count(Nat) where count(n).head() = n count(n).tail() = count(succ(n))

38/42

slide-135
SLIDE 135

Symmetric Support

data Nat where zero() : Nat succ(Nat) : Nat fun add(Nat, Nat) were add(zero(), n) = n add(succ(m), n) = succ(add(m, n)) codata Stream where Stream.head() : Nat Stream.tail() : Stream fun count(Nat) where count(n).head() = n count(n).tail() = count(succ(n))

38/42

slide-136
SLIDE 136

Symmetric Support

data Nat where zero() : Nat succ(Nat) : Nat fun add(Nat, Nat) were add(zero(), n) = n add(succ(m), n) = succ(add(m, n)) codata Stream where Stream.head() : Nat Stream.tail() : Stream fun count(Nat) where count(n).head() = n count(n).tail() = count(succ(n))

38/42

slide-137
SLIDE 137

Symmetric Support

data Nat where zero() : Nat succ(Nat) : Nat fun add(Nat, Nat) were add(zero(), n) = n add(succ(m), n) = succ(add(m, n)) codata Stream where Stream.head() : Nat Stream.tail() : Stream fun count(Nat) where count(n).head() = n count(n).tail() = count(succ(n))

38/42

slide-138
SLIDE 138

Symmetric Support

data Nat where zero() : Nat succ(Nat) : Nat fun add(Nat, Nat) were add(zero(), n) = n add(succ(m), n) = succ(add(m, n)) codata Stream where Stream.head() : Nat Stream.tail() : Stream fun count(Nat) where count(n).head() = n count(n).tail() = count(succ(n))

38/42

slide-139
SLIDE 139

Symmetric Support

data Nat where zero() : Nat succ(Nat) : Nat fun add(Nat, Nat) were add(zero(), n) = n add(succ(m), n) = succ(add(m, n)) codata Stream where Stream.head() : Nat Stream.tail() : Stream fun count(Nat) where count(n).head() = n count(n).tail() = count(succ(n))

38/42

slide-140
SLIDE 140

Symmetric Support

data Nat where zero() : Nat succ(Nat) : Nat fun add(Nat, Nat) were add(zero(), n) = n add(succ(m), n) = succ(add(m, n)) codata Stream where Stream.head() : Nat Stream.tail() : Stream fun count(Nat) where count(n).head() = n count(n).tail() = count(succ(n))

38/42

slide-141
SLIDE 141

Symmetric Support

data Nat where zero() : Nat succ(Nat) : Nat fun add(Nat, Nat) were add(zero(), n) = n add(succ(m), n) = succ(add(m, n)) codata Stream where Stream.head() : Nat Stream.tail() : Stream fun count(Nat) where count(n).head() = n count(n).tail() = count(succ(n))

38/42

slide-142
SLIDE 142

Symmetric Support

data Nat where zero() : Nat succ(Nat) : Nat fun add(Nat, Nat) were add(zero(), n) = n add(succ(m), n) = succ(add(m, n)) codata Stream where Stream.head() : Nat Stream.tail() : Stream fun count(Nat) where count(n).head() = n count(n).tail() = count(succ(n))

38/42

slide-143
SLIDE 143

Symmetric Support

data Nat where zero() : Nat succ(Nat) : Nat fun add(Nat, Nat) were add(zero(), n) = n add(succ(m), n) = succ(add(m, n)) codata Stream where Stream.head() : Nat Stream.tail() : Stream fun count(Nat) where count(n).head() = n count(n).tail() = count(succ(n))

38/42

slide-144
SLIDE 144

Symmetric Support

data Nat where zero() : Nat succ(Nat) : Nat fun add(Nat, Nat) were add(zero(), n) = n add(succ(m), n) = succ(add(m, n)) codata Stream where Stream.head() : Nat Stream.tail() : Stream fun count(Nat) where count(n).head() = n count(n).tail() = count(succ(n))

38/42

Abel, Pientka, Thibodeau, Setzer 2013 (Copatterns: Programming …)

slide-145
SLIDE 145

Very Simple

  • Purely functional.
  • Simple types.
  • No first-class functions.
  • No case expressions.
  • No cocase expressions.
  • No local variables.

39/42

slide-146
SLIDE 146

First Results

  • Support for full defunctionalization and

refunctionalization.

  • Programs can be written as matrices, so that (de|

re)functionalization both correspond to matrix transposition.

  • These matrices look like the matrices from the

expression and coexpression problems.

40/42

slide-147
SLIDE 147

Automatic Refunctionalization To a Language with Copattern Matching With Applications to the Expression Problem

Presentation at ICFP Tuesday, 4pm 41/42

slide-148
SLIDE 148

Summary

symmetric support for data and codata

Expression Problem

Refunctionalize Scala Dualize 42/42

slide-149
SLIDE 149

Summary

symmetric support for data and codata

Expression Problem

Refunctionalize Scala Dualize

Thanks