4 people like it.
Like the snippet!
Continuation Monad with Call/CC
This is an implementation of the Continuation monad using a type, taking an exception handler, and allowing for Call/CC. This specific implementation is mostly Matt Podwysocki's. I have a similar implementation using a purely functional, exception-handler-less version in FSharp.Monad. Until now, I haven't been able to resolve the callCC operator.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
|
type Cont<'a,'r> =
abstract Call : ('a -> 'r) * (exn -> 'r) -> 'r
let private protect f x cont econt =
let res = try Choice1Of2 (f x) with err -> Choice2Of2 err
match res with
| Choice1Of2 v -> cont v
| Choice2Of2 v -> econt v
let runCont (c:Cont<_,_>) cont econt = c.Call(cont, econt)
let throw exn = { new Cont<_,_> with member x.Call (cont,econt) = econt exn }
let callCC f =
{ new Cont<_,_> with
member x.Call(cont, econt) =
runCont (f (fun a -> { new Cont<_,_> with member x.Call(_,_) = cont a })) cont econt }
type ContinuationBuilder() =
member this.Return(a) =
{ new Cont<_,_> with member x.Call(cont, econt) = cont a }
member this.ReturnFrom(comp:Cont<_,_>) = comp
member this.Bind(comp1, f) =
{ new Cont<_,_> with
member x.Call (cont, econt) =
runCont comp1 (fun a -> protect f a (fun comp2 -> runCont comp2 cont econt) econt) econt }
member this.Catch(comp:Cont<_,_>) =
{ new Cont<Choice<_, exn>,_> with
member x.Call (cont, econt) =
runCont comp (fun v -> cont (Choice1Of2 v)) (fun err -> cont (Choice2Of2 err)) }
member this.Zero() =
this.Return ()
member this.TryWith(tryBlock, catchBlock) =
this.Bind(this.Catch tryBlock, (function Choice1Of2 v -> this.Return v
| Choice2Of2 exn -> catchBlock exn))
member this.TryFinally(tryBlock, finallyBlock) =
this.Bind(this.Catch tryBlock, (function Choice1Of2 v -> finallyBlock(); this.Return v
| Choice2Of2 exn -> finallyBlock(); throw exn))
member this.Using(res:#IDisposable, body) =
this.TryFinally(body res, (fun () -> match res with null -> () | disp -> disp.Dispose()))
member this.Combine(comp1, comp2) = this.Bind(comp1, (fun () -> comp2))
member this.Delay(f) = this.Bind(this.Return (), f)
member this.While(pred, body) =
if pred() then this.Bind(body, (fun () -> this.While(pred,body))) else this.Return ()
member this.For(items:seq<_>, body) =
this.Using(items.GetEnumerator(), (fun enum -> this.While((fun () -> enum.MoveNext()), this.Delay(fun () -> body enum.Current))))
let cont = ContinuationBuilder()
|
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
|
let c n = cont { return n }
let addSomeNumbers = cont {
let! x = c 6
let! y = c 7
return x + y }
[<Test>]
let ``When adding 6 to 7 and applying a continuation to convert to string and replace 1 with a, it should return a3``() =
runCont addSomeNumbers (fun x -> x.ToString().Replace('1', 'a')) (sprintf "%A") |> should equal "a3"
(* Test callCC *)
let sum l =
let rec sum l = cont {
let! result = callCC (fun exit1 -> cont {
match l with
| [] -> return 0
| h::t when h = 2 -> return! exit1 42
| h::t -> let! r = sum t
return h + r })
return result }
runCont (sum l) id (fun _ -> -1)
[<Test>]
let ``When summing a list without a 2 via callCC it should return 8``() =
sum [1;1;3;3] |> should equal 8
[<Test>]
let ``When summing a list containing 2 via callCC it should return 43``() =
sum [1;2;3] |> should equal 43
|
type Cont<'a,'r> =
interface
abstract member Call : ('a -> 'r) * (exn -> 'r) -> 'r
end
Full name: Script.Cont<_,_>
abstract member Cont.Call : ('a -> 'r) * (exn -> 'r) -> 'r
Full name: Script.Cont`2.Call
type exn = Exception
Full name: Microsoft.FSharp.Core.exn
val private protect : f:('a -> 'b) -> x:'a -> cont:('b -> 'c) -> econt:(exn -> 'c) -> 'c
Full name: Script.protect
val f : ('a -> 'b)
val x : 'a
val cont : ('b -> 'c)
val econt : (exn -> 'c)
val res : Choice<'b,exn>
union case Choice.Choice1Of2: 'T1 -> Choice<'T1,'T2>
val err : exn
union case Choice.Choice2Of2: 'T2 -> Choice<'T1,'T2>
val v : 'b
val v : exn
val runCont : c:Cont<'a,'b> -> cont:('a -> 'b) -> econt:(exn -> 'b) -> 'b
Full name: Script.runCont
val c : Cont<'a,'b>
val cont : ('a -> 'b)
val econt : (exn -> 'b)
abstract member Cont.Call : ('a -> 'r) * (exn -> 'r) -> 'r
val throw : exn:exn -> Cont<'a,'b>
Full name: Script.throw
Multiple items
val exn : exn
--------------------
type exn = Exception
Full name: Microsoft.FSharp.Core.exn
val x : Cont<'a,'b>
val callCC : f:(('a -> Cont<'b,'c>) -> #Cont<'a,'c>) -> Cont<'a,'c>
Full name: Script.callCC
val f : (('a -> Cont<'b,'c>) -> #Cont<'a,'c>)
val x : Cont<'a,'c>
val cont : ('a -> 'c)
val a : 'a
val x : Cont<'b,'c>
Multiple items
type ContinuationBuilder =
new : unit -> ContinuationBuilder
member Bind : comp1:Cont<'a1,'a2> * f:('a1 -> #Cont<'a4,'a2>) -> Cont<'a4,'a2>
member Catch : comp:Cont<'s,'t> -> Cont<Choice<'s,exn>,'t>
member Combine : comp1:Cont<unit,'h> * comp2:Cont<'i,'h> -> Cont<'i,'h>
member Delay : f:(unit -> #Cont<'f,'g>) -> Cont<'f,'g>
member For : items:seq<'a> * body:('a -> #Cont<unit,'c>) -> Cont<unit,'c>
member Return : a:'a7 -> Cont<'a7,'a8>
member ReturnFrom : comp:Cont<'a5,'a6> -> Cont<'a5,'a6>
member TryFinally : tryBlock:Cont<'n,'o> * finallyBlock:(unit -> unit) -> Cont<'n,'o>
member TryWith : tryBlock:Cont<'p,'q> * catchBlock:(exn -> Cont<'p,'q>) -> Cont<'p,'q>
...
Full name: Script.ContinuationBuilder
--------------------
new : unit -> ContinuationBuilder
val this : ContinuationBuilder
member ContinuationBuilder.Return : a:'a7 -> Cont<'a7,'a8>
Full name: Script.ContinuationBuilder.Return
val a : 'a7
val x : Cont<'a7,'a8>
val cont : ('a7 -> 'a8)
val econt : (exn -> 'a8)
member ContinuationBuilder.ReturnFrom : comp:Cont<'a5,'a6> -> Cont<'a5,'a6>
Full name: Script.ContinuationBuilder.ReturnFrom
val comp : Cont<'a5,'a6>
member ContinuationBuilder.Bind : comp1:Cont<'a1,'a2> * f:('a1 -> #Cont<'a4,'a2>) -> Cont<'a4,'a2>
Full name: Script.ContinuationBuilder.Bind
val comp1 : Cont<'a1,'a2>
val f : ('a1 -> #Cont<'a4,'a2>)
val x : Cont<'a4,'a2>
val cont : ('a4 -> 'a2)
val econt : (exn -> 'a2)
val a : 'a1
val comp2 : #Cont<'a4,'a2>
member ContinuationBuilder.Catch : comp:Cont<'s,'t> -> Cont<Choice<'s,exn>,'t>
Full name: Script.ContinuationBuilder.Catch
val comp : Cont<'s,'t>
Multiple items
type Choice<'T1,'T2> =
| Choice1Of2 of 'T1
| Choice2Of2 of 'T2
Full name: Microsoft.FSharp.Core.Choice<_,_>
--------------------
type Choice<'T1,'T2,'T3> =
| Choice1Of3 of 'T1
| Choice2Of3 of 'T2
| Choice3Of3 of 'T3
Full name: Microsoft.FSharp.Core.Choice<_,_,_>
--------------------
type Choice<'T1,'T2,'T3,'T4> =
| Choice1Of4 of 'T1
| Choice2Of4 of 'T2
| Choice3Of4 of 'T3
| Choice4Of4 of 'T4
Full name: Microsoft.FSharp.Core.Choice<_,_,_,_>
--------------------
type Choice<'T1,'T2,'T3,'T4,'T5> =
| Choice1Of5 of 'T1
| Choice2Of5 of 'T2
| Choice3Of5 of 'T3
| Choice4Of5 of 'T4
| Choice5Of5 of 'T5
Full name: Microsoft.FSharp.Core.Choice<_,_,_,_,_>
--------------------
type Choice<'T1,'T2,'T3,'T4,'T5,'T6> =
| Choice1Of6 of 'T1
| Choice2Of6 of 'T2
| Choice3Of6 of 'T3
| Choice4Of6 of 'T4
| Choice5Of6 of 'T5
| Choice6Of6 of 'T6
Full name: Microsoft.FSharp.Core.Choice<_,_,_,_,_,_>
--------------------
type Choice<'T1,'T2,'T3,'T4,'T5,'T6,'T7> =
| Choice1Of7 of 'T1
| Choice2Of7 of 'T2
| Choice3Of7 of 'T3
| Choice4Of7 of 'T4
| Choice5Of7 of 'T5
| Choice6Of7 of 'T6
| Choice7Of7 of 'T7
Full name: Microsoft.FSharp.Core.Choice<_,_,_,_,_,_,_>
val x : Cont<Choice<'s,exn>,'t>
val cont : (Choice<'s,exn> -> 't)
val econt : (exn -> 't)
val v : 's
member ContinuationBuilder.Zero : unit -> Cont<unit,'r>
Full name: Script.ContinuationBuilder.Zero
member ContinuationBuilder.Return : a:'a7 -> Cont<'a7,'a8>
member ContinuationBuilder.TryWith : tryBlock:Cont<'p,'q> * catchBlock:(exn -> Cont<'p,'q>) -> Cont<'p,'q>
Full name: Script.ContinuationBuilder.TryWith
val tryBlock : Cont<'p,'q>
val catchBlock : (exn -> Cont<'p,'q>)
member ContinuationBuilder.Bind : comp1:Cont<'a1,'a2> * f:('a1 -> #Cont<'a4,'a2>) -> Cont<'a4,'a2>
member ContinuationBuilder.Catch : comp:Cont<'s,'t> -> Cont<Choice<'s,exn>,'t>
val v : 'p
member ContinuationBuilder.TryFinally : tryBlock:Cont<'n,'o> * finallyBlock:(unit -> unit) -> Cont<'n,'o>
Full name: Script.ContinuationBuilder.TryFinally
val tryBlock : Cont<'n,'o>
val finallyBlock : (unit -> unit)
val v : 'n
member ContinuationBuilder.Using : res:'j * body:('j -> #Cont<'l,'m>) -> Cont<'l,'m> (requires 'j :> IDisposable and 'j : null)
Full name: Script.ContinuationBuilder.Using
val res : 'j (requires 'j :> IDisposable and 'j : null)
type IDisposable =
member Dispose : unit -> unit
Full name: System.IDisposable
val body : ('j -> #Cont<'l,'m>) (requires 'j :> IDisposable and 'j : null)
member ContinuationBuilder.TryFinally : tryBlock:Cont<'n,'o> * finallyBlock:(unit -> unit) -> Cont<'n,'o>
val disp : 'j (requires 'j :> IDisposable and 'j : null)
IDisposable.Dispose() : unit
member ContinuationBuilder.Combine : comp1:Cont<unit,'h> * comp2:Cont<'i,'h> -> Cont<'i,'h>
Full name: Script.ContinuationBuilder.Combine
val comp1 : Cont<unit,'h>
val comp2 : Cont<'i,'h>
member ContinuationBuilder.Delay : f:(unit -> #Cont<'f,'g>) -> Cont<'f,'g>
Full name: Script.ContinuationBuilder.Delay
val f : (unit -> #Cont<'f,'g>)
member ContinuationBuilder.While : pred:(unit -> bool) * body:Cont<unit,'d> -> Cont<unit,'d>
Full name: Script.ContinuationBuilder.While
val pred : (unit -> bool)
val body : Cont<unit,'d>
member ContinuationBuilder.While : pred:(unit -> bool) * body:Cont<unit,'d> -> Cont<unit,'d>
member ContinuationBuilder.For : items:seq<'a> * body:('a -> #Cont<unit,'c>) -> Cont<unit,'c>
Full name: Script.ContinuationBuilder.For
val items : seq<'a>
Multiple items
val seq : sequence:seq<'T> -> seq<'T>
Full name: Microsoft.FSharp.Core.Operators.seq
--------------------
type seq<'T> = Collections.Generic.IEnumerable<'T>
Full name: Microsoft.FSharp.Collections.seq<_>
val body : ('a -> #Cont<unit,'c>)
member ContinuationBuilder.Using : res:'j * body:('j -> #Cont<'l,'m>) -> Cont<'l,'m> (requires 'j :> IDisposable and 'j : null)
Collections.Generic.IEnumerable.GetEnumerator() : Collections.Generic.IEnumerator<'a>
val enum : Collections.Generic.IEnumerator<'a>
Collections.IEnumerator.MoveNext() : bool
member ContinuationBuilder.Delay : f:(unit -> #Cont<'f,'g>) -> Cont<'f,'g>
property Collections.Generic.IEnumerator.Current: 'a
val cont : ContinuationBuilder
Full name: Script.cont
val c : n:'a -> Cont<'a,'b>
Full name: Script.c
val n : 'a
val addSomeNumbers : Cont<int,string>
Full name: Script.addSomeNumbers
val x : int
val y : int
val ( When adding 6 to 7 and applying a continuation to convert to string and replace 1 with a, it should return a3 ) : unit -> 'a
Full name: Script.( When adding 6 to 7 and applying a continuation to convert to string and replace 1 with a, it should return a3 )
Int32.ToString() : string
Int32.ToString(provider: IFormatProvider) : string
Int32.ToString(format: string) : string
Int32.ToString(format: string, provider: IFormatProvider) : string
val sprintf : format:Printf.StringFormat<'T> -> 'T
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
val sum : l:int list -> int
Full name: Script.sum
val l : int list
val sum : (int list -> Cont<int,'a>)
val result : int
val exit1 : (int -> Cont<int,'a>)
val h : int
val t : int list
val r : int
val id : x:'T -> 'T
Full name: Microsoft.FSharp.Core.Operators.id
val ( When summing a list without a 2 via callCC it should return 8 ) : unit -> 'a
Full name: Script.( When summing a list without a 2 via callCC it should return 8 )
val ( When summing a list containing 2 via callCC it should return 43 ) : unit -> 'a
Full name: Script.( When summing a list containing 2 via callCC it should return 43 )
More information