Logic and Computation Lecture 4 Zena M. Ariola University of - - PowerPoint PPT Presentation

logic and computation
SMART_READER_LITE
LIVE PREVIEW

Logic and Computation Lecture 4 Zena M. Ariola University of - - PowerPoint PPT Presentation

Logic and Computation Lecture 4 Zena M. Ariola University of Oregon 24th Estonian Winter School in Computer Science, EWSCS 19 Lessons learned Construction-destruction in programming How to implement strong reduction efficiently How to


slide-1
SLIDE 1

Logic and Computation

Lecture 4

Zena M. Ariola

University of Oregon

24th Estonian Winter School in Computer Science, EWSCS ’19

slide-2
SLIDE 2

Lessons learned

Construction-destruction in programming How to implement strong reduction efficiently How to formulate join-point in an intermediate language Recursion and co-recursion A unifying theme for existing programming idioms: abstraction, session types, Church encodings, lazy evaluation and object-oriented programming Which connectives do you include in an intermediate language?

slide-3
SLIDE 3

Two dual approaches to

  • rganize information
slide-4
SLIDE 4

Data types

Defined by rules of creation (constructors). Like algebraic data types in ML and Haskell data Either a b where Lef :: a ⊢ Either a b Right :: b ⊢ Either a b Producer: fixed shapes given by constructors Lef(v1) Right(v2) Consumer: case analysis on constructions ˜ µ[Lef(x).c1| Right(y).c2] case v { Lef(x).v1 | Right(y).v2}

slide-5
SLIDE 5

Co-data types

Defined by rules of destruction (messages). codata a & b where π1 : a & b ⊢ a π2 : a & b ⊢ b Consumer: fixed shapes given by the destructors π1[e1] v.π1 π2[e2] v.π2 Producer: case analysis on destructors. “If I’m asked for first, do this” “If I’m asked for second, do that” µ(π1[α].c1|π2[β].c2) {π1 → v1, π2 → v2}

slide-6
SLIDE 6

Church encodings and O-O Programming

What do you want to do with a boolean?

slide-7
SLIDE 7

Church encodings and O-O Programming

What do you want to do with a boolean? If b then x else y codata Bool where If : Bool, a, a ⊢ a The producer will then patern match on the request: µ(If (x, y).v) or using an OO-syntax {If (x, y) → v} True and false then become: {If (x, y) → x} {If (x, y) → y} With co-paterns: true . If x y = x false . If x y = y

slide-8
SLIDE 8

Church encodings and O-O Programming

What do you want to do with a boolean? If b then x else y codata Bool where If : Bool, a, a ⊢ a The producer will then patern match on the request: µ(If (x, y).v) or using an OO-syntax {If (x, y) → v} True and false then become: {If (x, y) → x} {If (x, y) → y} With co-paterns: true . If x y = x false . If x y = y We arrive at the familiar encodings in the polymorphic λ-calculus: Bool = ∀a.a → a → a true = Λa.λx:a.λy:a.x false = Λa.λx:a.λy:a.y

slide-9
SLIDE 9

Church encodings and O-O Programming

What do you want to do with a boolean? If b then x else y codata Bool where If : Bool, a, a ⊢ a The producer will then patern match on the request: µ(If (x, y).v) or using an OO-syntax {If (x, y) → v} True and false then become: {If (x, y) → x} {If (x, y) → y} With co-paterns: true . If x y = x false . If x y = y We arrive at the familiar encodings in the polymorphic λ-calculus: Bool = ∀a.a → a → a true = Λa.λx:a.λy:a.x false = Λa.λx:a.λy:a.y The same applies to other data types - Visitor Patern

slide-10
SLIDE 10

Declaring functions as co-data

codata a → b where call : a → b, a ⊢ b Consumer: one destructor (function call)

Argument What to do with result

v · e (v1.call) v Producer: consider shape of the observation µ(x · α).c λx.v = µ(x · α).v| |α

slide-11
SLIDE 11

Control solves a dilemma

Extensionality is essential for observational properties about program M : A → B M = λx.M x

slide-12
SLIDE 12

Control solves a dilemma

Extensionality is essential for observational properties about program M : A → B M = λx.M x Evaluation to weak-head normal (i.e. a lambda) form avoids renaming

slide-13
SLIDE 13

Control solves a dilemma

Extensionality is essential for observational properties about program M : A → B M = λx.M x Evaluation to weak-head normal (i.e. a lambda) form avoids renaming Extensionality and weak reduction are inconsistent in call-by-name: λx.Ω x = Ω Ω loops forever while λx.Ω x is done.

slide-14
SLIDE 14

Control solves a dilemma

Extensionality is essential for observational properties about program M : A → B M = λx.M x Evaluation to weak-head normal (i.e. a lambda) form avoids renaming Extensionality and weak reduction are inconsistent in call-by-name: λx.Ω x = Ω Ω loops forever while λx.Ω x is done. Plotkin call-by-name continuation-passing style transformation (CPS) does not validates the η axiom: [ [M] ] [ [λx.M x] ] = λk.k(λx.λq. [ [M] ] (λv.v x q))

slide-15
SLIDE 15

Control solves a dilemma

Extensionality is essential for observational properties about program M : A → B M = λx.M x Evaluation to weak-head normal (i.e. a lambda) form avoids renaming Extensionality and weak reduction are inconsistent in call-by-name: λx.Ω x = Ω Ω loops forever while λx.Ω x is done. Plotkin call-by-name continuation-passing style transformation (CPS) does not validates the η axiom: [ [M] ] [ [λx.M x] ] = λk.k(λx.λq. [ [M] ] (λv.v x q))

Thielecke proves that η follows from parametricity Hoffman and Streicher have proposed an alternative CPS based on pairs [ [λx.M x] ] = λ(x, k). [ [M x] ] k = λ(x, k). [ [M] ] (x, k) = [ [M] ]

slide-16
SLIDE 16

Functions pattern match on the calling context

A function is not constructed, it is a destructor of the calling context λx.M µ[x · α].M| |α Recall let (x, y) = v in v′ = let z = vinv′[π1(z)/x, π2(z)/y] So... µ(x · α).c| |E = c[car E/x, cdr E/α] We then have: λx.Ω x

  • µ[x · α].Ω|

|x · α patern matching as projections µ[β].Ω| |car β · cdr β call stack is surjective µ[β].Ω| |β Ω

slide-17
SLIDE 17

A continuation is a data structure

The continuation is a data structure not a function. [ [M N] ] = λk. [ [M] ] λf .f [ [N] ] k [ [λx.M] ] = λk.k(λx. [ [M] ])

slide-18
SLIDE 18

A continuation is a data structure

The continuation is a data structure not a function. [ [M N] ] = λk. [ [M] ] λf .f [ [N] ] k [ [λx.M] ] = λk.k(λx. [ [M] ]) [ [M N] ] = λk. [ [M] ] ([ [N] ] , k) [ [λx.M] ] = λ(x, k). [ [M] ] k The same solution restores confluence to λ-calculi with control λx.(Abort 0) x Abort 0 λx.Abort 0

slide-19
SLIDE 19

Sequent calculus inspired a new intermediate language

slide-20
SLIDE 20

Sharing of code - Join Points

if x > 100 : print "x is large" else : print "x is small" print "goodbye"

x > 100 print "x␣ is ␣ large " print "x␣ is ␣small" print "goodbye" yes no

Join Point: A common point where several branches of control flow join together (φ node in SSA)

slide-21
SLIDE 21

Sharing of code - Join Points

if x > 100 : print "x is large" else : print "x is small" print "goodbye"

x > 100 print "x␣ is ␣ large " print "x␣ is ␣small" print "goodbye" yes no

Join Point: A common point where several branches of control flow join together (φ node in SSA) Disadvantage of continuation-passing style: bakes in the evaluation strategy Join points are very special functions:

slide-22
SLIDE 22

Sharing of code - Join Points

if x > 100 : print "x is large" else : print "x is small" print "goodbye"

x > 100 print "x␣ is ␣ large " print "x␣ is ␣small" print "goodbye" yes no

Join Point: A common point where several branches of control flow join together (φ node in SSA) Disadvantage of continuation-passing style: bakes in the evaluation strategy Join points are very special functions: a) Always tail-called, don’t return b) Never escape their scope Different operational reading: just a jump to a labeled block of code Join points are more efficient to implement, less costly than a full closure

slide-23
SLIDE 23

Case-of-Case and Friends

In Core: let j x y = big in not(case z of A x y → j x y B → False) ⇓ let j x y = big in case z of A x y → not (j x y) B → not False This is bad! The join point is ruined (j no longer tail-called)

slide-24
SLIDE 24

Case-of-Case and Friends

In Core with join points: let j x y = big in not(case z of A x y → j x y B → False) ⇓ let j x y = not big in case z of A x y → j x y B → not False This is much beter! The join point is preserved!

slide-25
SLIDE 25

Core with join points

The current intermediate language of Haskell has join points (FJ) FJ is still in correspondence with minimal logic; it doesn’t have control Join points are preserved through optimizations We replace Flanagan et al. moto: Think in CPS, work in direct style with Think in sequent calculus, work in direct style.

slide-26
SLIDE 26

Recursion and Corecursion

In recursion, data is constructed and the consumer is a process that uses the data Data is finite and its use is (potentially) infinite Recursion uses the data, rather than producing it Recursion starts big, and reduces down to a base case

slide-27
SLIDE 27

Recursion and Corecursion

In recursion, data is constructed and the consumer is a process that uses the data Data is finite and its use is (potentially) infinite Recursion uses the data, rather than producing it Recursion starts big, and reduces down to a base case In corecursion, the use of data is constructed and the producer is a process that generates the data The use of data is finite and its creation is (potentially) infinite Corecursion produces the data, rather than using it Corecursion starts from a seed and produces bigger and bigger internal state

slide-28
SLIDE 28

Recursion on List

data List = Nil | Cons Nat List Data is finite, but we need recursion in the consumer (like in G¨

  • del System T):

E ::= α | ˜ µx.c | v.E | rec(Nil ⇒ vb, Cons(x, xs) ⇒ y.v)[E]

slide-29
SLIDE 29

Recursion on List

data List = Nil | Cons Nat List Data is finite, but we need recursion in the consumer (like in G¨

  • del System T):

E ::= α | ˜ µx.c | v.E | rec(Nil ⇒ vb, Cons(x, xs) ⇒ y.v)[E]

Nil| |rec(Nil ⇒ vb, Cons(x, xs) ⇒ y.v)[E] → vb| |E Cons(x, xs)| |rec(Nil ⇒ vb, Cons(x, xs) ⇒ y.v)[E] → xs| |rec(Nil ⇒ vb, Cons(x, xs) ⇒ y.v)[˜ µy.v| |E]

slide-30
SLIDE 30

Recursion on List

data List = Nil | Cons Nat List Data is finite, but we need recursion in the consumer (like in G¨

  • del System T):

E ::= α | ˜ µx.c | v.E | rec(Nil ⇒ vb, Cons(x, xs) ⇒ y.v)[E]

Nil| |rec(Nil ⇒ vb, Cons(x, xs) ⇒ y.v)[E] → vb| |E Cons(x, xs)| |rec(Nil ⇒ vb, Cons(x, xs) ⇒ y.v)[E] → xs| |rec(Nil ⇒ vb, Cons(x, xs) ⇒ y.v)[˜ µy.v| |E]

length = λ(x, α).x| |rec(Nil ⇒ 0, Cons(_, xs) ⇒ y.y + 1)[α], which we abbreviate as λ(x, α).x| |rec(0, y.y + 1)[α]

slide-31
SLIDE 31

Recursion on List

data List = Nil | Cons Nat List Data is finite, but we need recursion in the consumer (like in G¨

  • del System T):

E ::= α | ˜ µx.c | v.E | rec(Nil ⇒ vb, Cons(x, xs) ⇒ y.v)[E]

Nil| |rec(Nil ⇒ vb, Cons(x, xs) ⇒ y.v)[E] → vb| |E Cons(x, xs)| |rec(Nil ⇒ vb, Cons(x, xs) ⇒ y.v)[E] → xs| |rec(Nil ⇒ vb, Cons(x, xs) ⇒ y.v)[˜ µy.v| |E]

length = λ(x, α).x| |rec(Nil ⇒ 0, Cons(_, xs) ⇒ y.y + 1)[α], which we abbreviate as λ(x, α).x| |rec(0, y.y + 1)[α]

Example Cons(1, Cons(2, Cons(3, Nil)))| |rec(0, _.y.y + 1)[tp] → Cons(2, Cons(3, Nil))| |rec(0, _.y.y + 1)[˜ µy.y + 1| |tp] → Cons(3, Nil)| |rec(0, _.y.y + 1)[˜ µy.y + 1| |˜ µy.y + 1| |tp] → Nil| |rec(0, _.y.y + 1)[˜ µy.y + 1| |˜ µy.y + 1| |˜ µy.y + 1| |tp] → 0| |˜ µy.y + 1| |˜ µy.y + 1| |˜ µy.y + 1| |tp → 1| |˜ µy.y + 1| |˜ µy.y + 1| |tp → 2| |˜ µy.y + 1| |tp → 3| |tp

slide-32
SLIDE 32

Co-recursion

codata Stream where Head : Nat Tail : Stream Codata is finite, but we need co-recursion in the producer: rec(Nil ⇒ vb, Cons(x, xs) ⇒ y.v)[E]

slide-33
SLIDE 33

Co-recursion

codata Stream where Head : Nat Tail : Stream Codata is finite, but we need co-recursion in the producer: rec(Nil ⇒ vb, Cons(x, xs) ⇒ y.v)[E] corec(Head[α] ⇒ ev, Tail[α] ⇒ β.e)[v] Cons(x, xs)| |rec(Nil ⇒ vb, Cons(x, xs) ⇒ y.v)[E] → xs| |rec(Nil ⇒ vb, Cons(x, xs) ⇒ y.v)[˜ µy.v| |E

corec(Head[α] ⇒ eb, Tail[α] ⇒ β.e)[V]| |Head[E] → V| |eb[E/α] corec(Head[α] ⇒ eb, Tail[α] ⇒ β.e)[V]| |Tail[E] → corec(Head[α] ⇒ eb, Tail[α] ⇒ β.e)[µβ.V| |e[E/α]]| |E

slide-34
SLIDE 34

Infinite objects

corec(Head[α] ⇒ eb, Tail[α] ⇒ β.e)[V]| |Head[E] → V| |eb[E/α] corec(Head[α] ⇒ eb, Tail[α] ⇒ β.e)[V]| |Tail[E] → corec(Head[α] ⇒ eb, Tail[α] ⇒ β.e)[µβ.V| |e[E/α]]| |E

Example The stream’s of 0′s is represented as corec(Head[α] ⇒ α, Tail[α] ⇒ β.β)[0] The set co-nat of natural numbers plus ∞ is represented as conat=corec(Head[α] ⇒ α, Tail[α] ⇒ β.˜ µx.x + 1| |β)[0] Let’s take the third elements of conat:

corec(Head[α] ⇒ α, Tail[α] ⇒ β.˜ µx.x + 1| |β)[0]| |Tail.Tail.Head.tp → corec(Head[α] ⇒ α, Tail[α] ⇒ β.˜ µx.x + 1| |β)[µβ.0| |˜ µx.x + 1| |β| |Tail.Head.tp → corec(Head[α] ⇒ α, Tail[α] ⇒ β.˜ µx.x + 1| |β)[µβ.1| |β]| |Tail.Head.tp → corec(Head[α] ⇒ α, Tail[α] ⇒ β.˜ µx.x + 1| |β)[1]| |Tail.Head.tp → corec(Head[α] ⇒ α, Tail[α] ⇒ β.˜ µx.x + 1| |β)[2]| |Head.tp → 2| |tp

slide-35
SLIDE 35

Codata as a unifying concept

slide-36
SLIDE 36

Demand Driven Programming

Hughes in "Why Functional Programming maters" motivates the utility of practical functional programming through its excellence in compositionality

slide-37
SLIDE 37

Demand Driven Programming

Hughes in "Why Functional Programming maters" motivates the utility of practical functional programming through its excellence in compositionality Compositionality is reached through the technique of demand-driven programming

slide-38
SLIDE 38

Demand Driven Programming

Hughes in "Why Functional Programming maters" motivates the utility of practical functional programming through its excellence in compositionality Compositionality is reached through the technique of demand-driven programming Haskell uses lazy evaluation to implement demand-driven programming.

slide-39
SLIDE 39

Demand Driven Programming

Hughes in "Why Functional Programming maters" motivates the utility of practical functional programming through its excellence in compositionality Compositionality is reached through the technique of demand-driven programming Haskell uses lazy evaluation to implement demand-driven programming. Disadvantages?

slide-40
SLIDE 40

Demand Driven Programming

Hughes in "Why Functional Programming maters" motivates the utility of practical functional programming through its excellence in compositionality Compositionality is reached through the technique of demand-driven programming Haskell uses lazy evaluation to implement demand-driven programming. Disadvantages? A language should directly support the capability of yielding control to the consumer independently of the language being strict or lazy.

slide-41
SLIDE 41

Demand Driven Programming

Hughes in "Why Functional Programming maters" motivates the utility of practical functional programming through its excellence in compositionality Compositionality is reached through the technique of demand-driven programming Haskell uses lazy evaluation to implement demand-driven programming. Disadvantages? A language should directly support the capability of yielding control to the consumer independently of the language being strict or lazy.

Codata is a general, portable, programming feature which is the key for compositionality in program design.

slide-42
SLIDE 42

Abstraction Mechanism

Reynolds identified two different mechanisms to achieve abstraction: abstract data types and procedural abstraction. Abstract data types are crisply expressed by existential types What is the essence of procedure abstraction?

slide-43
SLIDE 43

Abstraction Mechanism

Reynolds identified two different mechanisms to achieve abstraction: abstract data types and procedural abstraction. Abstract data types are crisply expressed by existential types What is the essence of procedure abstraction? CODATA!

slide-44
SLIDE 44

Abstraction Mechanism

Reynolds identified two different mechanisms to achieve abstraction: abstract data types and procedural abstraction. Abstract data types are crisply expressed by existential types What is the essence of procedure abstraction? CODATA! Example

codata Set where IsEmpty : Set −> Bool Contains : Set −> Int −> Bool Insert : Set −> Int −> Set Union : Set −> Set −> Set finiteSet : List Int −> Set ( finiteSet xs ). IsEmpty = xs == [] ( finiteSet xs ). Contains y = elemOf xs y ( finiteSet xs ). Insert y = finiteSet (y:xs) ( finiteSet xs ). Union s = fold (\ x t −> t. Insert x) s xs emptySet = finiteSet []

slide-45
SLIDE 45

Representing Pre- and Post-Conditions

The extension of data types with indexes has proven useful to statically verify a data structure’s invariant, like for red-black trees With indexed data types, a programmer can constrain the way an object is constructed.

slide-46
SLIDE 46

Representing Pre- and Post-Conditions

The extension of data types with indexes has proven useful to statically verify a data structure’s invariant, like for red-black trees With indexed data types, a programmer can constrain the way an object is constructed. With indexed codata types, a programmer can constrain the way an object is going to be used In a language with type indexes, codata enables a programmer to express more information in the interface of an abstraction. Example

index Raw, Bound, Live codata Socket i where Bind : Socket Raw −> String −> Socket Bound Connect : Socket Bound −> Socket Live Send : Socket Live −> String −> () Receive : Socket Live −> String Close : Socket Live −> ()

slide-47
SLIDE 47

Some aspects of session Types

Internal and external choice correspond to data and codata.

slide-48
SLIDE 48

Some aspects of session Types

Internal and external choice correspond to data and codata. Choosing between internal and external choice corresponds to the expression problem

slide-49
SLIDE 49

Some aspects of session Types

Internal and external choice correspond to data and codata. Choosing between internal and external choice corresponds to the expression problem The same call-and-return dialog between a client and server occurs in both data and codata Example

queue = ?{ enq : ? string .queue , deq : !{ none: unit , some: ! string .queue } }

can be translated as:

codata Qeue where Enq : Qeue −> (String −> Qeue) Deq : Qeue −> Answer data Answer where None : () −> Answer Some : ( String , Qeue) −> Answer

slide-50
SLIDE 50

Compiling data and codata

Which connectives should we have in an intermediate language to support any data and codata type the user may define in a classical call-by-name or by-value language?

slide-51
SLIDE 51

Compiling data and codata

Which connectives should we have in an intermediate language to support any data and codata type the user may define in a classical call-by-name or by-value language? Positive: ⊕, 0 ⊗, 1 0 has zero constructors and the eliminator is ˜ µ(). In natural deduction form case M of . no right rule Γ | ˜ µ() : 0 ⊢ ∆ 0L Γ ⊢ () : 1 | ∆ 1R c : Γ ⊢ ∆ Γ | ˜ µ().c : 1 ⊢ ∆ 1L Negative: &, ⊤ `, ⊥ The ⊤ is the dual of 0. no lef rule Γ ⊢ µ() : ⊤ ⊢ ∆ ⊤R Γ | tp : ⊥ ⊢ ∆ ⊥L c : (Γ ⊢ ∆) Γ ⊢ µ(tp).c : ⊥ | ∆ ⊥R and shifs.