continuation passing style for effect handlers - formal ... ·...
TRANSCRIPT
This work is supported by
Continuation Passing Style for Effect HandlersFormal Structures for Computation and Deduction
Daniel Hillerström 1 Sam Lindley 1
Robert Atkey 2 KC Sivaramakrishnan 3
1The University of Edinburgh, UK
2University of Strathclyde, UK
3University of Cambridge, UK
September 5, 2017
Algebraic effects by example: a drunk coin toss
Consider two effects: nondeterminism and exceptions
drunkToss : Toss ! {Choose : Bool;Fail : Zero}drunkToss = if do Choose then
if do Choose then Heads else Tailselse absurd do Fail
Drunk toss computation tree
Algebraic effects by example: a drunk coin toss
Consider two effects: nondeterminism and exceptions
drunkToss : Toss ! {Choose : Bool;Fail : Zero}drunkToss = if do Choose then
if do Choose then Heads else Tailselse absurd do Fail
Drunk toss computation treeChoose
Choose
Heads
true
Tails
false
true
Fail
false
Effect handlers by example: modular interpretations
Choose handler
allChoices : α ! {Choose : Bool; ρ} ⇒ List α ! {ρ}allChoices = return x 7→ [x ]
Choose r 7→ r true ++ r false
Fail handler
fail : α ! {Fail : Zero; ρ} ⇒ List α ! {ρ}fail = return x 7→ [x ]
Fail r 7→ []
Two possible interpretations:
Effect handlers by example: modular interpretations
Choose handler
allChoices : α ! {Choose : Bool; ρ} ⇒ List α ! {ρ}allChoices = return x 7→ [x ]
Choose r 7→ r true ++ r false
Fail handler
fail : α ! {Fail : Zero; ρ} ⇒ List α ! {ρ}fail = return x 7→ [x ]
Fail r 7→ []
Two possible interpretations:
handle (handle drunkToss with fail) with allChoices⇒ [[Heads], [Tails], []]
handle (handle drunkToss with allChoices) with fail⇒ []
Effect handlers by example: modular interpretations
Choose handler
allChoices : α ! {Choose : Bool; ρ} ⇒ List α ! {ρ}allChoices = return x 7→ [x ]
Choose r 7→ r true ++ r false
Fail handler
fail : α ! {Fail : Zero; ρ} ⇒ List α ! {ρ}fail = return x 7→ [x ]
Fail r 7→ []
Two possible interpretations:
handle (handle with fail) with allChoicesChoose
Choose
Heads
true
Tails
false
true
Fail
false
Effect handlers by example: modular interpretations
Choose handler
allChoices : α ! {Choose : Bool; ρ} ⇒ List α ! {ρ}allChoices = return x 7→ [x ]
Choose r 7→ r true ++ r false
Fail handler
fail : α ! {Fail : Zero; ρ} ⇒ List α ! {ρ}fail = return x 7→ [x ]
Fail r 7→ []
Two possible interpretations:
handle (handle with fail) with allChoicesChoose
Choose
Heads
true
Tails
false
true
Fail
false
Effect handlers by example: modular interpretations
Choose handler
allChoices : α ! {Choose : Bool; ρ} ⇒ List α ! {ρ}allChoices = return x 7→ [x ]
Choose r 7→ r true ++ r false
Fail handler
fail : α ! {Fail : Zero; ρ} ⇒ List α ! {ρ}fail = return x 7→ [x ]
Fail r 7→ []
Two possible interpretations:
handle (handle with fail) with allChoicesChoose
Choose
[[Heads]]
true
Tails
false
true
Fail
false
Effect handlers by example: modular interpretations
Choose handler
allChoices : α ! {Choose : Bool; ρ} ⇒ List α ! {ρ}allChoices = return x 7→ [x ]
Choose r 7→ r true ++ r false
Fail handler
fail : α ! {Fail : Zero; ρ} ⇒ List α ! {ρ}fail = return x 7→ [x ]
Fail r 7→ []
Two possible interpretations:
handle (handle with fail) with allChoicesChoose
Choose
[[Heads]]
true
[[Tails]]
false
true
Fail
false
Effect handlers by example: modular interpretations
Choose handler
allChoices : α ! {Choose : Bool; ρ} ⇒ List α ! {ρ}allChoices = return x 7→ [x ]
Choose r 7→ r true ++ r false
Fail handler
fail : α ! {Fail : Zero; ρ} ⇒ List α ! {ρ}fail = return x 7→ [x ]
Fail r 7→ []
Two possible interpretations:
handle (handle with fail) with allChoicesChoose
[[Heads], [Tails]]
true
Fail
false
Effect handlers by example: modular interpretations
Choose handler
allChoices : α ! {Choose : Bool; ρ} ⇒ List α ! {ρ}allChoices = return x 7→ [x ]
Choose r 7→ r true ++ r false
Fail handler
fail : α ! {Fail : Zero; ρ} ⇒ List α ! {ρ}fail = return x 7→ [x ]
Fail r 7→ []
Two possible interpretations:
handle (handle with fail) with allChoicesChoose
[[Heads], [Tails]]
true
Fail
false
Effect handlers by example: modular interpretations
Choose handler
allChoices : α ! {Choose : Bool; ρ} ⇒ List α ! {ρ}allChoices = return x 7→ [x ]
Choose r 7→ r true ++ r false
Fail handler
fail : α ! {Fail : Zero; ρ} ⇒ List α ! {ρ}fail = return x 7→ [x ]
Fail r 7→ []
Two possible interpretations:
handle (handle with fail) with allChoicesChoose
[[Heads], [Tails]]
true
[[]]
false
Effect handlers by example: modular interpretations
Choose handler
allChoices : α ! {Choose : Bool; ρ} ⇒ List α ! {ρ}allChoices = return x 7→ [x ]
Choose r 7→ r true ++ r false
Fail handler
fail : α ! {Fail : Zero; ρ} ⇒ List α ! {ρ}fail = return x 7→ [x ]
Fail r 7→ []
Two possible interpretations:
[[Heads], [Tails], []]
Effect handlers by example: modular interpretations
Choose handler
allChoices : α ! {Choose : Bool; ρ} ⇒ List α ! {ρ}allChoices = return x 7→ [x ]
Choose r 7→ r true ++ r false
Fail handler
fail : α ! {Fail : Zero; ρ} ⇒ List α ! {ρ}fail = return x 7→ [x ]
Fail r 7→ []
Two possible interpretations:
handle (handle drunkToss with allChoices) with fail⇒ []
Effect handlers by example: modular interpretations
Choose handler
allChoices : α ! {Choose : Bool; ρ} ⇒ List α ! {ρ}allChoices = return x 7→ [x ]
Choose r 7→ r true ++ r false
Fail handler
fail : α ! {Fail : Zero; ρ} ⇒ List α ! {ρ}fail = return x 7→ [x ]
Fail r 7→ []
Two possible interpretations:
handle (handle with allChoices) with failChoose
Choose
[Heads]
true
[Tails]
false
true
Fail
false
Effect handlers by example: modular interpretations
Choose handler
allChoices : α ! {Choose : Bool; ρ} ⇒ List α ! {ρ}allChoices = return x 7→ [x ]
Choose r 7→ r true ++ r false
Fail handler
fail : α ! {Fail : Zero; ρ} ⇒ List α ! {ρ}fail = return x 7→ [x ]
Fail r 7→ []
Two possible interpretations:
handle (handle with allChoices) with failChoose
[Heads,Tails]
true
Fail
false
Effect handlers by example: modular interpretations
Choose handler
allChoices : α ! {Choose : Bool; ρ} ⇒ List α ! {ρ}allChoices = return x 7→ [x ]
Choose r 7→ r true ++ r false
Fail handler
fail : α ! {Fail : Zero; ρ} ⇒ List α ! {ρ}fail = return x 7→ [x ]
Fail r 7→ []
Two possible interpretations:
handle (handle with allChoices) with failChoose
[Heads,Tails]
true
Fail
false
Effect handlers by example: modular interpretations
Choose handler
allChoices : α ! {Choose : Bool; ρ} ⇒ List α ! {ρ}allChoices = return x 7→ [x ]
Choose r 7→ r true ++ r false
Fail handler
fail : α ! {Fail : Zero; ρ} ⇒ List α ! {ρ}fail = return x 7→ [x ]
Fail r 7→ []
Two possible interpretations:
[]
Operational semantics for effect handlers
The operational semantics for handlers is fairly simple
handle (return V ) with H N[V /x ],where Hret = {return x 7→ N}
handle E [do ` V ] with H N[V /p, λy . handle E [return y ] with H/r ],where ` /∈ BL(E) and H` = {` p r 7→ N}
The set of bound labels, BL, is defined inductively
BL([ ]) = ∅ BL(let x ← E in N) = BL(E) BL(handle E with H) = BL(E) ∪ dom(H)
Evaluation contexts, E , are standard
E ::= [] | let x ← E in N | handle E with H
Why continuation passing style?
So, effect handlers are great! Now, how do we compile them?
Why continuation passing style (CPS)?CPS is good for implementing control flowCPS is formulated on a restricted subset of λ-calculusThe Links compiler (Cooper, Lindley, Wadler, and Yallop 2006)
Server-side uses a CEK machineClient-side uses CPS
Source and target calculi
Our source calculus is λρeff (Hillerström and Lindley 2016).
Values V ,W ::= x | λx .M | 〈〉 | 〈` = V ;W 〉 | (`V )
Computations M,N ::= V W| let 〈` = x ; y〉 = V in N | case V {` x 7→ M; y 7→ N} | absurdV| return V | let x ← M in N| do ` V | handle M with H
Handlers H ::= {return x 7→ M} | {` p r 7→ M} ] H}
Our target is an untyped λ-calculus
Values V ,W ::= x | λx .M | 〈〉 | 〈` = V ;W 〉 | ` VComputations M,N ::= V | M W | let 〈` = x ; y〉 = V in N
| case V {` x 7→ M; y 7→ N} | absurdV
Source and target calculi
Our source calculus is λρeff (Hillerström and Lindley 2016).
Values V ,W ::= x | λx .M | 〈〉 | 〈` = V ;W 〉 | (`V )
Computations M,N ::= V W| let 〈` = x ; y〉 = V in N | case V {` x 7→ M; y 7→ N} | absurdV| return V | let x ← M in N| do ` V | handle M with H
Handlers H ::= {return x 7→ M} | {` p r 7→ M} ] H}
Our target is an untyped λ-calculus
Values V ,W ::= x | λx .M | 〈〉 | 〈` = V ;W 〉 | ` VComputations M,N ::= V | M W | let 〈` = x ; y〉 = V in N
| case V {` x 7→ M; y 7→ N} | absurdV
Source and target calculi
Our source calculus is λρeff (Hillerström and Lindley 2016).
Values V ,W ::= x | λx .M | 〈〉 | 〈` = V ;W 〉 | (`V )
Computations M,N ::= V W| let 〈` = x ; y〉 = V in N | case V {` x 7→ M; y 7→ N} | absurdV| return V | let x ← M in N| do ` V | handle M with H
Handlers H ::= {return x 7→ M} | {` p r 7→ M} ] H}
Our target is an untyped λ-calculus
Values V ,W ::= x | λx .M | 〈〉 | 〈` = V ;W 〉 | ` VComputations M,N ::= V | M W | let 〈` = x ; y〉 = V in N
| case V {` x 7→ M; y 7→ N} | absurdV
Source and target calculi
Our source calculus is λρeff (Hillerström and Lindley 2016).
Values V ,W ::= x | λx .M | 〈〉 | 〈` = V ;W 〉 | (`V )
Computations M,N ::= V W| let 〈` = x ; y〉 = V in N | case V {` x 7→ M; y 7→ N} | absurdV| return V | let x ← M in N| do ` V | handle M with H
Handlers H ::= {return x 7→ M} | {` p r 7→ M} ] H}
Our target is an untyped λ-calculus
Values V ,W ::= x | λx .M | 〈〉 | 〈` = V ;W 〉 | ` VComputations M,N ::= V | M W | let 〈` = x ; y〉 = V in N
| case V {` x 7→ M; y 7→ N} | absurdV
Curried CPS translation for fine-grain call-by-value
The continuation represents the execution context.
Computations
JV W K = JV K JW KJlet 〈` = x ; y〉 = V in NK = let 〈` = x ; y〉 = JV K in JNK
Jcase V {` x 7→ M; y 7→ N}K = case JV K {` x 7→ JMK; y 7→ JNK}Jabsurd V K = absurd JV KJreturn V K = λk.k JV K
Jlet x ← M in NK = λk.JMK(λx .JNKk)
ValuesJxK = x
Jλx .MK = λx .JMKJ〈〉K = 〈〉
J〈` = V ;W 〉K = 〈` = JV K; JW K〉J` V K = ` JV K
Curried CPS for handlers
With handlers there are two contexts: a pure and an effect context.Idea: implicit stack of alternating pure and effect continuations.
Jdo ` V K = λk.λh.h (` 〈JV K, λx .k x h〉)Jhandle M with HK = JMK JHretK JHopsKJ{return x 7→ N}K = λx .λh.JNK
J{` p r 7→ N`}`∈LK = λz .case z {(` 〈p, r〉 7→ JN`K)`∈L; y 7→ Mforward}Mforward = λk ′.λh′.vmap (λ〈p, r〉 k.k 〈p, λx .r x k ′ h′〉) y h′
>JMK = JMK (λx .λh.x) (λz .absurd z)
Problem: J−K yields administrative redexes and is not properly tail-recursive!
>Jreturn 〈〉K = (λk.k 〈〉) (λx .λh.x) (λz .absurd z)
((λx .λh.x) 〈〉) (λz .absurd z) (λh.〈〉) (λz .absurd z) 〈〉
Curried CPS for handlers
With handlers there are two contexts: a pure and an effect context.Idea: implicit stack of alternating pure and effect continuations.
Jdo ` V K = λk.λh.h (` 〈JV K, λx .k x h〉)Jhandle M with HK = JMK JHretK JHopsKJ{return x 7→ N}K = λx .λh.JNK
J{` p r 7→ N`}`∈LK = λz .case z {(` 〈p, r〉 7→ JN`K)`∈L; y 7→ Mforward}Mforward = λk ′.λh′.vmap (λ〈p, r〉 k.k 〈p, λx .r x k ′ h′〉) y h′
>JMK = JMK (λx .λh.x) (λz .absurd z)
Problem: J−K yields administrative redexes and is not properly tail-recursive!
>Jreturn 〈〉K = (λk.k 〈〉) (λx .λh.x) (λz .absurd z)
((λx .λh.x) 〈〉) (λz .absurd z) (λh.〈〉) (λz .absurd z) 〈〉
Curried CPS for handlers
With handlers there are two contexts: a pure and an effect context.Idea: implicit stack of alternating pure and effect continuations.
Jdo ` V K = λk.λh.h (` 〈JV K, λx .k x h〉)Jhandle M with HK = JMK JHretK JHopsKJ{return x 7→ N}K = λx .λh.JNK
J{` p r 7→ N`}`∈LK = λz .case z {(` 〈p, r〉 7→ JN`K)`∈L; y 7→ Mforward}Mforward = λk ′.λh′.vmap (λ〈p, r〉 k.k 〈p, λx .r x k ′ h′〉) y h′
>JMK = JMK (λx .λh.x) (λz .absurd z)
Problem: J−K yields administrative redexes and is not properly tail-recursive!
>Jreturn 〈〉K = (λk.k 〈〉) (λx .λh.x) (λz .absurd z)
((λx .λh.x) 〈〉) (λz .absurd z) (λh.〈〉) (λz .absurd z) 〈〉
Curried CPS for handlers
With handlers there are two contexts: a pure and an effect context.Idea: implicit stack of alternating pure and effect continuations.
Jdo ` V K = λk.λh.h (` 〈JV K, λx .k x h〉)Jhandle M with HK = JMK JHretK JHopsKJ{return x 7→ N}K = λx .λh.JNK
J{` p r 7→ N`}`∈LK = λz .case z {(` 〈p, r〉 7→ JN`K)`∈L; y 7→ Mforward}Mforward = λk ′.λh′.vmap (λ〈p, r〉 k.k 〈p, λx .r x k ′ h′〉) y h′
>JMK = JMK (λx .λh.x) (λz .absurd z)
Problem: J−K yields administrative redexes and is not properly tail-recursive!
>Jreturn 〈〉K = (λk.k 〈〉) (λx .λh.x) (λz .absurd z)
((λx .λh.x) 〈〉) (λz .absurd z) (λh.〈〉) (λz .absurd z) 〈〉
Curried CPS for handlers
With handlers there are two contexts: a pure and an effect context.Idea: implicit stack of alternating pure and effect continuations.
Jdo ` V K = λk.λh.h (` 〈JV K, λx .k x h〉)Jhandle M with HK = JMK JHretK JHopsKJ{return x 7→ N}K = λx .λh.JNK
J{` p r 7→ N`}`∈LK = λz .case z {(` 〈p, r〉 7→ JN`K)`∈L; y 7→ Mforward}Mforward = λk ′.λh′.vmap (λ〈p, r〉 k.k 〈p, λx .r x k ′ h′〉) y h′
>JMK = JMK (λx .λh.x) (λz .absurd z)
Problem: J−K yields administrative redexes and is not properly tail-recursive!
>Jreturn 〈〉K = (λk.k 〈〉) (λx .λh.x) (λz .absurd z)
((λx .λh.x) 〈〉) (λz .absurd z) (λh.〈〉) (λz .absurd z) 〈〉
Curried CPS for handlers
With handlers there are two contexts: a pure and an effect context.Idea: implicit stack of alternating pure and effect continuations.
Jdo ` V K = λk.λh.h (` 〈JV K, λx .k x h〉)Jhandle M with HK = JMK JHretK JHopsKJ{return x 7→ N}K = λx .λh.JNK
J{` p r 7→ N`}`∈LK = λz .case z {(` 〈p, r〉 7→ JN`K)`∈L; y 7→ Mforward}Mforward = λk ′.λh′.vmap (λ〈p, r〉 k.k 〈p, λx .r x k ′ h′〉) y h′
>JMK = JMK (λx .λh.x) (λz .absurd z)
Problem: J−K yields administrative redexes and is not properly tail-recursive!
>Jreturn 〈〉K = (λk.k 〈〉) (λx .λh.x) (λz .absurd z) ((λx .λh.x) 〈〉) (λz .absurd z)
(λh.〈〉) (λz .absurd z) 〈〉
Curried CPS for handlers
With handlers there are two contexts: a pure and an effect context.Idea: implicit stack of alternating pure and effect continuations.
Jdo ` V K = λk.λh.h (` 〈JV K, λx .k x h〉)Jhandle M with HK = JMK JHretK JHopsKJ{return x 7→ N}K = λx .λh.JNK
J{` p r 7→ N`}`∈LK = λz .case z {(` 〈p, r〉 7→ JN`K)`∈L; y 7→ Mforward}Mforward = λk ′.λh′.vmap (λ〈p, r〉 k.k 〈p, λx .r x k ′ h′〉) y h′
>JMK = JMK (λx .λh.x) (λz .absurd z)
Problem: J−K yields administrative redexes and is not properly tail-recursive!
>Jreturn 〈〉K = (λk.k 〈〉) (λx .λh.x) (λz .absurd z) ((λx .λh.x) 〈〉) (λz .absurd z) (λh.〈〉) (λz .absurd z)
〈〉
Curried CPS for handlers
With handlers there are two contexts: a pure and an effect context.Idea: implicit stack of alternating pure and effect continuations.
Jdo ` V K = λk.λh.h (` 〈JV K, λx .k x h〉)Jhandle M with HK = JMK JHretK JHopsKJ{return x 7→ N}K = λx .λh.JNK
J{` p r 7→ N`}`∈LK = λz .case z {(` 〈p, r〉 7→ JN`K)`∈L; y 7→ Mforward}Mforward = λk ′.λh′.vmap (λ〈p, r〉 k.k 〈p, λx .r x k ′ h′〉) y h′
>JMK = JMK (λx .λh.x) (λz .absurd z)
Problem: J−K yields administrative redexes and is not properly tail-recursive!
>Jreturn 〈〉K = (λk.k 〈〉) (λx .λh.x) (λz .absurd z) ((λx .λh.x) 〈〉) (λz .absurd z) (λh.〈〉) (λz .absurd z) 〈〉
Uncurried CPS for handlers
Idea: make the continuation stack explicit (Materzok and Biernacki 2012).
Jreturn V K = λ(k :: ks).k JV K ksJlet x ← M in NK = λ(k :: ks).JMK((λx ks.JNK(k :: ks)) :: ks)
Jdo ` V K = λ(k :: h :: ks).h (` 〈JV K, h :: k :: []〉) ksJhandle M with HK = λks.JMK(JHretK :: JHopsK :: ks)J{return x 7→ N}K = λx ks.let (h :: ks ′) = ks in JNK ks
J{(` p r 7→ N`)`∈L}K = λz ks.case z {(` 〈p, s〉 7→ let r = fun s in JN`K ks)`∈L;y 7→ Mforward}
Mforward = let (k ′ :: h′ :: ks ′) = ks invmap (λ〈p, r〉 (k :: ks).k 〈p, h′ :: k ′ :: r〉 ks) y ks ′
>JMK = JMK ((λx ks.x) :: (λz ks.absurd z) :: [])
But J−K still yields the administrative redexes
>Jreturn 〈〉K = (λ(k :: ks).k 〈〉 ks) ((λx ks.x) :: (λz .absurd z) :: [])
(λx ks.x) 〈〉 [(λz .absurd z)] + 〈〉
Uncurried CPS for handlers
Idea: make the continuation stack explicit (Materzok and Biernacki 2012).
Jreturn V K = λ(k :: ks).k JV K ksJlet x ← M in NK = λ(k :: ks).JMK((λx ks.JNK(k :: ks)) :: ks)
Jdo ` V K = λ(k :: h :: ks).h (` 〈JV K, h :: k :: []〉) ksJhandle M with HK = λks.JMK(JHretK :: JHopsK :: ks)J{return x 7→ N}K = λx ks.let (h :: ks ′) = ks in JNK ks
J{(` p r 7→ N`)`∈L}K = λz ks.case z {(` 〈p, s〉 7→ let r = fun s in JN`K ks)`∈L;y 7→ Mforward}
Mforward = let (k ′ :: h′ :: ks ′) = ks invmap (λ〈p, r〉 (k :: ks).k 〈p, h′ :: k ′ :: r〉 ks) y ks ′
>JMK = JMK ((λx ks.x) :: (λz ks.absurd z) :: [])
But J−K still yields the administrative redexes
>Jreturn 〈〉K = (λ(k :: ks).k 〈〉 ks) ((λx ks.x) :: (λz .absurd z) :: [])
(λx ks.x) 〈〉 [(λz .absurd z)] + 〈〉
Uncurried CPS for handlers
Idea: make the continuation stack explicit (Materzok and Biernacki 2012).
Jreturn V K = λ(k :: ks).k JV K ksJlet x ← M in NK = λ(k :: ks).JMK((λx ks.JNK(k :: ks)) :: ks)
Jdo ` V K = λ(k :: h :: ks).h (` 〈JV K, h :: k :: []〉) ksJhandle M with HK = λks.JMK(JHretK :: JHopsK :: ks)J{return x 7→ N}K = λx ks.let (h :: ks ′) = ks in JNK ks
J{(` p r 7→ N`)`∈L}K = λz ks.case z {(` 〈p, s〉 7→ let r = fun s in JN`K ks)`∈L;y 7→ Mforward}
Mforward = let (k ′ :: h′ :: ks ′) = ks invmap (λ〈p, r〉 (k :: ks).k 〈p, h′ :: k ′ :: r〉 ks) y ks ′
>JMK = JMK ((λx ks.x) :: (λz ks.absurd z) :: [])
But J−K still yields the administrative redexes
>Jreturn 〈〉K = (λ(k :: ks).k 〈〉 ks) ((λx ks.x) :: (λz .absurd z) :: [])
(λx ks.x) 〈〉 [(λz .absurd z)] + 〈〉
Uncurried CPS for handlers
Idea: make the continuation stack explicit (Materzok and Biernacki 2012).
Jreturn V K = λ(k :: ks).k JV K ksJlet x ← M in NK = λ(k :: ks).JMK((λx ks.JNK(k :: ks)) :: ks)
Jdo ` V K = λ(k :: h :: ks).h (` 〈JV K, h :: k :: []〉) ksJhandle M with HK = λks.JMK(JHretK :: JHopsK :: ks)J{return x 7→ N}K = λx ks.let (h :: ks ′) = ks in JNK ks
J{(` p r 7→ N`)`∈L}K = λz ks.case z {(` 〈p, s〉 7→ let r = fun s in JN`K ks)`∈L;y 7→ Mforward}
Mforward = let (k ′ :: h′ :: ks ′) = ks invmap (λ〈p, r〉 (k :: ks).k 〈p, h′ :: k ′ :: r〉 ks) y ks ′
>JMK = JMK ((λx ks.x) :: (λz ks.absurd z) :: [])
But J−K still yields the administrative redexes
>Jreturn 〈〉K = (λ(k :: ks).k 〈〉 ks) ((λx ks.x) :: (λz .absurd z) :: [])
(λx ks.x) 〈〉 [(λz .absurd z)] + 〈〉
Uncurried CPS for handlers
Idea: make the continuation stack explicit (Materzok and Biernacki 2012).
Jreturn V K = λ(k :: ks).k JV K ksJlet x ← M in NK = λ(k :: ks).JMK((λx ks.JNK(k :: ks)) :: ks)
Jdo ` V K = λ(k :: h :: ks).h (` 〈JV K, h :: k :: []〉) ksJhandle M with HK = λks.JMK(JHretK :: JHopsK :: ks)J{return x 7→ N}K = λx ks.let (h :: ks ′) = ks in JNK ks
J{(` p r 7→ N`)`∈L}K = λz ks.case z {(` 〈p, s〉 7→ let r = fun s in JN`K ks)`∈L;y 7→ Mforward}
Mforward = let (k ′ :: h′ :: ks ′) = ks invmap (λ〈p, r〉 (k :: ks).k 〈p, h′ :: k ′ :: r〉 ks) y ks ′
>JMK = JMK ((λx ks.x) :: (λz ks.absurd z) :: [])
But J−K still yields the administrative redexes
>Jreturn 〈〉K = (λ(k :: ks).k 〈〉 ks) ((λx ks.x) :: (λz .absurd z) :: [])
(λx ks.x) 〈〉 [(λz .absurd z)] + 〈〉
Uncurried CPS for handlers
Idea: make the continuation stack explicit (Materzok and Biernacki 2012).
Jreturn V K = λ(k :: ks).k JV K ksJlet x ← M in NK = λ(k :: ks).JMK((λx ks.JNK(k :: ks)) :: ks)
Jdo ` V K = λ(k :: h :: ks).h (` 〈JV K, h :: k :: []〉) ksJhandle M with HK = λks.JMK(JHretK :: JHopsK :: ks)J{return x 7→ N}K = λx ks.let (h :: ks ′) = ks in JNK ks
J{(` p r 7→ N`)`∈L}K = λz ks.case z {(` 〈p, s〉 7→ let r = fun s in JN`K ks)`∈L;y 7→ Mforward}
Mforward = let (k ′ :: h′ :: ks ′) = ks invmap (λ〈p, r〉 (k :: ks).k 〈p, h′ :: k ′ :: r〉 ks) y ks ′
>JMK = JMK ((λx ks.x) :: (λz ks.absurd z) :: [])
But J−K still yields the administrative redexes
>Jreturn 〈〉K = (λ(k :: ks).k 〈〉 ks) ((λx ks.x) :: (λz .absurd z) :: []) (λx ks.x) 〈〉 [(λz .absurd z)]
+ 〈〉
Uncurried CPS for handlers
Idea: make the continuation stack explicit (Materzok and Biernacki 2012).
Jreturn V K = λ(k :: ks).k JV K ksJlet x ← M in NK = λ(k :: ks).JMK((λx ks.JNK(k :: ks)) :: ks)
Jdo ` V K = λ(k :: h :: ks).h (` 〈JV K, h :: k :: []〉) ksJhandle M with HK = λks.JMK(JHretK :: JHopsK :: ks)J{return x 7→ N}K = λx ks.let (h :: ks ′) = ks in JNK ks
J{(` p r 7→ N`)`∈L}K = λz ks.case z {(` 〈p, s〉 7→ let r = fun s in JN`K ks)`∈L;y 7→ Mforward}
Mforward = let (k ′ :: h′ :: ks ′) = ks invmap (λ〈p, r〉 (k :: ks).k 〈p, h′ :: k ′ :: r〉 ks) y ks ′
>JMK = JMK ((λx ks.x) :: (λz ks.absurd z) :: [])
But J−K still yields the administrative redexes
>Jreturn 〈〉K = (λ(k :: ks).k 〈〉 ks) ((λx ks.x) :: (λz .absurd z) :: []) (λx ks.x) 〈〉 [(λz .absurd z)] + 〈〉
Higher-order CPS for handlers (I)
Idea: adopt a two level λ-calculus (Danvy and Filinski 1990; Danvy and Nielsen2003) with two kinds of continuations
Static continuations κDynamic continuations k
Correspondingly, two kinds of reductionsStatic reductions at translation timeDynamic reductions at run-time
Move between levels using reflection (↑) and reification (↓)
↓↑V = V↓(V :: VS) = V :: ↓VS
Higher-order CPS for handlers (II)
Two kinds of reductions: static and dynamic .
Jreturn V K = λκ :: κs.κ@ JV K @ ↓κsJlet x ← M in NK = λκ :: κs.JMK @ ((λx ks.JNK @ (κ :: ↑ks)) :: κs)
Jdo ` V K = λκ :: η :: κs.η @ (` 〈JV K, η :: κ :: []〉) @ ↓κsJhandle M with HK = λκs.JMK @ (JHretK :: JHopsK :: κs)
J{return x 7→ N}K = λx ks.let (h :: ks ′) = ks in JNK @ ↑ks ′J{(` p r 7→ N`)`∈L}K = λz ks.case z {(` 〈p, s〉 7→ let r = fun s in JN`K @ ↑ks)`∈L;
y 7→ Mforward}Mforward = let (k ′ :: h′ :: ks ′) = ks in
vmap (λ〈p, s〉 (k :: ks).k 〈p, h′ :: k ′ :: s〉 ks) y ks ′
>JMK = JMK @ ((λx ks.x) :: (λz ks.absurd z) :: ↑[])
J−K produces no statically eliminatable administrative redexes
>Jreturn 〈〉K = (λκ :: κs.κ@ 〈〉@ ↓κ) @ ((λx ks.x) :: (λz ks.absurd z) :: ↑[])
= (λx ks.x) @ 〈〉@ ↓((λz ks.absurd z) :: ↑[])= (λx ks.x) @ 〈〉@ ((λz ks.absurd z) :: []) + 〈〉
Higher-order CPS for handlers (II)
Two kinds of reductions: static and dynamic .
Jreturn V K = λκ :: κs.κ@ JV K @ ↓κsJlet x ← M in NK = λκ :: κs.JMK @ ((λx ks.JNK @ (κ :: ↑ks)) :: κs)
Jdo ` V K = λκ :: η :: κs.η @ (` 〈JV K, η :: κ :: []〉) @ ↓κsJhandle M with HK = λκs.JMK @ (JHretK :: JHopsK :: κs)
J{return x 7→ N}K = λx ks.let (h :: ks ′) = ks in JNK @ ↑ks ′J{(` p r 7→ N`)`∈L}K = λz ks.case z {(` 〈p, s〉 7→ let r = fun s in JN`K @ ↑ks)`∈L;
y 7→ Mforward}Mforward = let (k ′ :: h′ :: ks ′) = ks in
vmap (λ〈p, s〉 (k :: ks).k 〈p, h′ :: k ′ :: s〉 ks) y ks ′
>JMK = JMK @ ((λx ks.x) :: (λz ks.absurd z) :: ↑[])
J−K produces no statically eliminatable administrative redexes
>Jreturn 〈〉K = (λκ :: κs.κ@ 〈〉@ ↓κ) @ ((λx ks.x) :: (λz ks.absurd z) :: ↑[])
= (λx ks.x) @ 〈〉@ ↓((λz ks.absurd z) :: ↑[])= (λx ks.x) @ 〈〉@ ((λz ks.absurd z) :: []) + 〈〉
Higher-order CPS for handlers (II)
Two kinds of reductions: static and dynamic .
Jreturn V K = λκ :: κs.κ@ JV K @ ↓κsJlet x ← M in NK = λκ :: κs.JMK @ ((λx ks.JNK @ (κ :: ↑ks)) :: κs)
Jdo ` V K = λκ :: η :: κs.η @ (` 〈JV K, η :: κ :: []〉) @ ↓κsJhandle M with HK = λκs.JMK @ (JHretK :: JHopsK :: κs)
J{return x 7→ N}K = λx ks.let (h :: ks ′) = ks in JNK @ ↑ks ′J{(` p r 7→ N`)`∈L}K = λz ks.case z {(` 〈p, s〉 7→ let r = fun s in JN`K @ ↑ks)`∈L;
y 7→ Mforward}Mforward = let (k ′ :: h′ :: ks ′) = ks in
vmap (λ〈p, s〉 (k :: ks).k 〈p, h′ :: k ′ :: s〉 ks) y ks ′
>JMK = JMK @ ((λx ks.x) :: (λz ks.absurd z) :: ↑[])
J−K produces no statically eliminatable administrative redexes
>Jreturn 〈〉K = (λκ :: κs.κ@ 〈〉@ ↓κ) @ ((λx ks.x) :: (λz ks.absurd z) :: ↑[])
= (λx ks.x) @ 〈〉@ ↓((λz ks.absurd z) :: ↑[])= (λx ks.x) @ 〈〉@ ((λz ks.absurd z) :: []) + 〈〉
Higher-order CPS for handlers (II)
Two kinds of reductions: static and dynamic .
Jreturn V K = λκ :: κs.κ@ JV K @ ↓κsJlet x ← M in NK = λκ :: κs.JMK @ ((λx ks.JNK @ (κ :: ↑ks)) :: κs)
Jdo ` V K = λκ :: η :: κs.η @ (` 〈JV K, η :: κ :: []〉) @ ↓κsJhandle M with HK = λκs.JMK @ (JHretK :: JHopsK :: κs)
J{return x 7→ N}K = λx ks.let (h :: ks ′) = ks in JNK @ ↑ks ′J{(` p r 7→ N`)`∈L}K = λz ks.case z {(` 〈p, s〉 7→ let r = fun s in JN`K @ ↑ks)`∈L;
y 7→ Mforward}Mforward = let (k ′ :: h′ :: ks ′) = ks in
vmap (λ〈p, s〉 (k :: ks).k 〈p, h′ :: k ′ :: s〉 ks) y ks ′
>JMK = JMK @ ((λx ks.x) :: (λz ks.absurd z) :: ↑[])
J−K produces no statically eliminatable administrative redexes
>Jreturn 〈〉K = (λκ :: κs.κ@ 〈〉@ ↓κ) @ ((λx ks.x) :: (λz ks.absurd z) :: ↑[])
= (λx ks.x) @ 〈〉@ ↓((λz ks.absurd z) :: ↑[])= (λx ks.x) @ 〈〉@ ((λz ks.absurd z) :: []) + 〈〉
Higher-order CPS for handlers (II)
Two kinds of reductions: static and dynamic .
Jreturn V K = λκ :: κs.κ@ JV K @ ↓κsJlet x ← M in NK = λκ :: κs.JMK @ ((λx ks.JNK @ (κ :: ↑ks)) :: κs)
Jdo ` V K = λκ :: η :: κs.η @ (` 〈JV K, η :: κ :: []〉) @ ↓κsJhandle M with HK = λκs.JMK @ (JHretK :: JHopsK :: κs)
J{return x 7→ N}K = λx ks.let (h :: ks ′) = ks in JNK @ ↑ks ′J{(` p r 7→ N`)`∈L}K = λz ks.case z {(` 〈p, s〉 7→ let r = fun s in JN`K @ ↑ks)`∈L;
y 7→ Mforward}Mforward = let (k ′ :: h′ :: ks ′) = ks in
vmap (λ〈p, s〉 (k :: ks).k 〈p, h′ :: k ′ :: s〉 ks) y ks ′
>JMK = JMK @ ((λx ks.x) :: (λz ks.absurd z) :: ↑[])
J−K produces no statically eliminatable administrative redexes
>Jreturn 〈〉K = (λκ :: κs.κ@ 〈〉@ ↓κ) @ ((λx ks.x) :: (λz ks.absurd z) :: ↑[])
= (λx ks.x) @ 〈〉@ ↓((λz ks.absurd z) :: ↑[])= (λx ks.x) @ 〈〉@ ((λz ks.absurd z) :: []) + 〈〉
Higher-order CPS for handlers (II)
Two kinds of reductions: static and dynamic .
Jreturn V K = λκ :: κs.κ@ JV K @ ↓κsJlet x ← M in NK = λκ :: κs.JMK @ ((λx ks.JNK @ (κ :: ↑ks)) :: κs)
Jdo ` V K = λκ :: η :: κs.η @ (` 〈JV K, η :: κ :: []〉) @ ↓κsJhandle M with HK = λκs.JMK @ (JHretK :: JHopsK :: κs)
J{return x 7→ N}K = λx ks.let (h :: ks ′) = ks in JNK @ ↑ks ′J{(` p r 7→ N`)`∈L}K = λz ks.case z {(` 〈p, s〉 7→ let r = fun s in JN`K @ ↑ks)`∈L;
y 7→ Mforward}Mforward = let (k ′ :: h′ :: ks ′) = ks in
vmap (λ〈p, s〉 (k :: ks).k 〈p, h′ :: k ′ :: s〉 ks) y ks ′
>JMK = JMK @ ((λx ks.x) :: (λz ks.absurd z) :: ↑[])
J−K produces no statically eliminatable administrative redexes
>Jreturn 〈〉K = (λκ :: κs.κ@ 〈〉@ ↓κ) @ ((λx ks.x) :: (λz ks.absurd z) :: ↑[])= (λx ks.x) @ 〈〉@ ↓((λz ks.absurd z) :: ↑[])
= (λx ks.x) @ 〈〉@ ((λz ks.absurd z) :: []) + 〈〉
Higher-order CPS for handlers (II)
Two kinds of reductions: static and dynamic .
Jreturn V K = λκ :: κs.κ@ JV K @ ↓κsJlet x ← M in NK = λκ :: κs.JMK @ ((λx ks.JNK @ (κ :: ↑ks)) :: κs)
Jdo ` V K = λκ :: η :: κs.η @ (` 〈JV K, η :: κ :: []〉) @ ↓κsJhandle M with HK = λκs.JMK @ (JHretK :: JHopsK :: κs)
J{return x 7→ N}K = λx ks.let (h :: ks ′) = ks in JNK @ ↑ks ′J{(` p r 7→ N`)`∈L}K = λz ks.case z {(` 〈p, s〉 7→ let r = fun s in JN`K @ ↑ks)`∈L;
y 7→ Mforward}Mforward = let (k ′ :: h′ :: ks ′) = ks in
vmap (λ〈p, s〉 (k :: ks).k 〈p, h′ :: k ′ :: s〉 ks) y ks ′
>JMK = JMK @ ((λx ks.x) :: (λz ks.absurd z) :: ↑[])
J−K produces no statically eliminatable administrative redexes
>Jreturn 〈〉K = (λκ :: κs.κ@ 〈〉@ ↓κ) @ ((λx ks.x) :: (λz ks.absurd z) :: ↑[])= (λx ks.x) @ 〈〉@ ↓((λz ks.absurd z) :: ↑[])= (λx ks.x) @ 〈〉@ ((λz ks.absurd z) :: [])
+ 〈〉
Higher-order CPS for handlers (II)
Two kinds of reductions: static and dynamic .
Jreturn V K = λκ :: κs.κ@ JV K @ ↓κsJlet x ← M in NK = λκ :: κs.JMK @ ((λx ks.JNK @ (κ :: ↑ks)) :: κs)
Jdo ` V K = λκ :: η :: κs.η @ (` 〈JV K, η :: κ :: []〉) @ ↓κsJhandle M with HK = λκs.JMK @ (JHretK :: JHopsK :: κs)
J{return x 7→ N}K = λx ks.let (h :: ks ′) = ks in JNK @ ↑ks ′J{(` p r 7→ N`)`∈L}K = λz ks.case z {(` 〈p, s〉 7→ let r = fun s in JN`K @ ↑ks)`∈L;
y 7→ Mforward}Mforward = let (k ′ :: h′ :: ks ′) = ks in
vmap (λ〈p, s〉 (k :: ks).k 〈p, h′ :: k ′ :: s〉 ks) y ks ′
>JMK = JMK @ ((λx ks.x) :: (λz ks.absurd z) :: ↑[])
J−K produces no statically eliminatable administrative redexes
>Jreturn 〈〉K = (λκ :: κs.κ@ 〈〉@ ↓κ) @ ((λx ks.x) :: (λz ks.absurd z) :: ↑[])= (λx ks.x) @ 〈〉@ ↓((λz ks.absurd z) :: ↑[])= (λx ks.x) @ 〈〉@ ((λz ks.absurd z) :: []) + 〈〉
Correctness
Definition (Translation of evaluation contexts)
J[ ]K = λκs. κsJlet x ← E in NK = λκ :: κs. JEK @ ((λx ks.JNK @ (k :: ↑ks)) :: κs)
Jhandle E with HK = λκs. JEK @ (JHretK :: JHopsK :: κs)
Lemma (Decomposition)The characteristic property of the CPS translation on evaluation contexts
JE [M]K @ (V :: VS) = JMK @ (JEK @ (V :: VS))
Theorem (Simulation)If M N then >JMK + ∗
a >JNK.
Conclusions
In summaryhandlers provide a modular abstraction for user-defined computational effects,first full CPS translations for handlers,the first-order curried CPS is neat, but inpractical,and the first-order uncurried CPS is naïve,refectified by a higher-order uncurried CPS.
Full details in the paper along witha discussion on the implementation of the higher-order CPS,a sketch of a typed higher-order CPS translation,an adaptation to accommodate shallow handlers,and a discussion on adapting the translation to a language like OCaml.
An implementation is available in Links
https://github.com/links-lang/links
References
Danvy, Olivier and Andrzej Filinski (1990). “Abstracting Control”. In: LISP andFunctional Programming, pp. 151–160.
Danvy, Olivier and Lasse R. Nielsen (2003). “A first-order one-pass CPStransformation”. In: Theor. Comput. Sci. 308.1-3, pp. 239–257.
Cooper, Ezra et al. (2006). “Links: Web Programming Without Tiers”. In:FMCO. Vol. 4709. LNCS. Springer, pp. 266–296.
Materzok, Marek and Dariusz Biernacki (2012). “A Dynamic Interpretation of theCPS Hierarchy”. In: APLAS. Vol. 7705. LNCS. Springer, pp. 296–311.
Hillerström, Daniel and Sam Lindley (2016). “Liberating effects with rows andhandlers”. In: TyDe@ICFP. ACM, pp. 15–27.