6 people like it.
Like the snippet!
Session Types in F#
An implementation of session types in F#. Inspired from the paper "Haskell Session Types with (Almost) No Class"
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:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
|
// http://www.eecs.harvard.edu/~tov/pubs/haskell-session-types/session08.pdf
// Basic Operations
[<AbstractClass>]
type Ops = class end
type Eps = class inherit Ops end
type Send<'T, 'Rest when 'Rest :> Ops> = class inherit Ops end
type Recv<'T, 'Rest when 'Rest :> Ops> = class inherit Ops end
type Choose<'Left, 'Right when 'Left :> Ops and 'Right :> Ops> = class inherit Ops end
type Offer<'Left, 'Right when 'Left :> Ops and 'Right :> Ops> = class inherit Ops end
// Client/Server Duality
type Dual<'Client, 'Server when 'Client :> Ops and 'Server :> Ops> private() =
static member Eps = new Dual<Eps, Eps>()
member self.Send<'T>() = new Dual<Send<'T, 'Client>, Recv<'T, 'Server>>()
member self.Recv<'T>() = new Dual<Recv<'T, 'Client>, Send<'T, 'Server>>()
member self.Choose<'Client', 'Server' when 'Client' :> Ops and 'Server' :> Ops>(dual : Dual<'Client', 'Server'>) =
new Dual<Choose<'Client, 'Client'>, Offer<'Server, 'Server'>>()
member self.Offer<'Client', 'Server' when 'Client' :> Ops and 'Server' :> Ops>(dual : Dual<'Client', 'Server'>) =
new Dual<Offer<'Client, 'Client'>, Choose<'Server, 'Server'>>()
let eps = Dual<_, _>.Eps
// Session Parameterized Monad
type Msg = Msg of (obj * AsyncReplyChannel<unit>)
type Session<'S1, 'S2, 'T> = Session of (MailboxProcessor<Msg> -> Async<'T>)
type SessionBuilder() =
member self.Return (value : 'T) : Session<'S, 'S, 'T> =
Session (fun _ -> async { return value })
member self.Bind (session : Session<'S1, 'S2, 'T>, f : 'T -> Session<'S2, 'S3, 'R>)
: Session<'S1, 'S3, 'R> =
Session (fun mailBox ->
async {
let (Session r) = session
let! x = r mailBox
let (Session r') = f x
return! r' mailBox
})
let session = new SessionBuilder()
// Basic Operations
let send (value : 'T) : Session<Send<'T, 'Rest>, 'Rest, unit> =
Session (fun mailBox ->
async { return! mailBox.PostAndAsyncReply (fun reply -> Msg (value :> obj, reply)) })
let recv () : Session<Recv<'T, 'Rest>, 'Rest, 'T> =
Session (fun mailBox ->
async {
let! (Msg (value, reply)) = mailBox.Receive()
reply.Reply ()
return value :?> 'T
})
let sel1 () : Session<Choose<'First, 'Second>, 'First, unit> =
Session (fun mailBox ->
async { return! mailBox.PostAndAsyncReply (fun reply -> Msg (true :> obj, reply)) })
let sel2 () : Session<Choose<'First, 'Second>, 'Second, unit> =
Session (fun mailBox ->
async { return! mailBox.PostAndAsyncReply (fun reply -> Msg (false :> obj, reply)) })
let cases (left : Session<'First, 'Rest, 'T>) (right : Session<'Second, 'Rest, 'T>)
: Session<Offer<'First, 'Second>, 'Rest, 'T> =
Session (fun mailBox ->
async {
let! (Msg (value, reply)) = mailBox.Receive()
reply.Reply ()
match value :?> bool with
| true ->
let (Session r) = left
return! r mailBox
| false ->
let (Session r) = right
return! r mailBox
})
let run (dual : Dual<'Client, 'Server>)
(client : Session<'Client, Eps, 'T>)
(server : Session<'Server, Eps, unit>) =
let mailBox = MailboxProcessor.Start(fun _ -> async { () })
let (Session r) = server
let (Session r') = client
let result =
[|async { let! _ = r mailBox in return Unchecked.defaultof<'T> }; r' mailBox|]
|> Async.Parallel |> Async.RunSynchronously
result.[1]
// Example
let addDual = eps.Recv<int>().Send<int>().Send<int>()
let stringToIntDual = eps.Recv<int>().Send<string>()
let chooseDual = addDual.Choose(stringToIntDual)
type AddSessionClient = Send<int, Send<int, Recv<int, Eps>>>
let clientAdd : Session<AddSessionClient, Eps, int> =
session {
do! send 1
do! send 2
let! result = recv ()
return result
}
type AddSessionServer = Recv<int, Recv<int, Send<int, Eps>>>
let serverAdd : Session<AddSessionServer, Eps, unit> =
session {
let! first = recv ()
let! second = recv ()
do! send (first + second)
}
type StringToIntSessionClient = Send<string, Recv<int, Eps>>
let clientStringToInt : Session<StringToIntSessionClient, Eps, int> =
session {
do! send "42"
let! result = recv ()
return result
}
type StringToIntServer = Recv<string, Send<int, Eps>>
let serverStringToInt : Session<StringToIntServer, Eps, unit> =
session {
let! value = recv ()
do! send (System.Int32.Parse value)
}
let client (input : int) : Session<Choose<AddSessionClient, StringToIntSessionClient>, Eps, int> =
session {
if input = 1 then
do! sel1 ()
let! value = clientAdd
return value
else
do! sel2 ()
let! value = clientStringToInt
return value
}
let server : Session<Offer<AddSessionServer, StringToIntServer>, Eps, unit> =
cases serverAdd serverStringToInt
run addDual clientAdd serverAdd // 3
run stringToIntDual clientStringToInt serverStringToInt // 42
run chooseDual (client 1) server // 3
run chooseDual (client 2) server // 42
|
Multiple items
type AbstractClassAttribute =
inherit Attribute
new : unit -> AbstractClassAttribute
Full name: Microsoft.FSharp.Core.AbstractClassAttribute
--------------------
new : unit -> AbstractClassAttribute
type Ops
Full name: Script.Ops
type Eps =
inherit Ops
Full name: Script.Eps
type Send<'T,'Rest (requires 'Rest :> Ops)> =
inherit Ops
Full name: Script.Send<_,_>
type Recv<'T,'Rest (requires 'Rest :> Ops)> =
inherit Ops
Full name: Script.Recv<_,_>
type Choose<'Left,'Right (requires 'Left :> Ops and 'Right :> Ops)> =
inherit Ops
Full name: Script.Choose<_,_>
type Offer<'Left,'Right (requires 'Left :> Ops and 'Right :> Ops)> =
inherit Ops
Full name: Script.Offer<_,_>
Multiple items
type Dual<'Client,'Server (requires 'Client :> Ops and 'Server :> Ops)> =
private new : unit -> Dual<'Client,'Server>
member Choose : dual:Dual<'Client','Server'> -> Dual<Choose<'Client,'Client'>,Offer<'Server,'Server'>> (requires 'Client' :> Ops and 'Server' :> Ops)
member Offer : dual:Dual<'Client','Server'> -> Dual<Offer<'Client,'Client'>,Choose<'Server,'Server'>> (requires 'Client' :> Ops and 'Server' :> Ops)
member Recv : unit -> Dual<Recv<'T,'Client>,Send<'T,'Server>>
member Send : unit -> Dual<Send<'T,'Client>,Recv<'T,'Server>>
static member Eps : Dual<Eps,Eps>
Full name: Script.Dual<_,_>
--------------------
private new : unit -> Dual<'Client,'Server>
Multiple items
static member Dual.Eps : Dual<Eps,Eps>
Full name: Script.Dual`2.Eps
--------------------
type Eps =
inherit Ops
Full name: Script.Eps
val self : Dual<#Ops,#Ops>
Multiple items
member Dual.Send : unit -> Dual<Send<'T,'Client>,Recv<'T,'Server>>
Full name: Script.Dual`2.Send
--------------------
type Send<'T,'Rest (requires 'Rest :> Ops)> =
inherit Ops
Full name: Script.Send<_,_>
Multiple items
member Dual.Recv : unit -> Dual<Recv<'T,'Client>,Send<'T,'Server>>
Full name: Script.Dual`2.Recv
--------------------
type Recv<'T,'Rest (requires 'Rest :> Ops)> =
inherit Ops
Full name: Script.Recv<_,_>
Multiple items
member Dual.Choose : dual:Dual<'Client','Server'> -> Dual<Choose<'Client,'Client'>,Offer<'Server,'Server'>> (requires 'Client' :> Ops and 'Server' :> Ops)
Full name: Script.Dual`2.Choose
--------------------
type Choose<'Left,'Right (requires 'Left :> Ops and 'Right :> Ops)> =
inherit Ops
Full name: Script.Choose<_,_>
val dual : Dual<#Ops,#Ops>
Multiple items
member Dual.Offer : dual:Dual<'Client','Server'> -> Dual<Offer<'Client,'Client'>,Choose<'Server,'Server'>> (requires 'Client' :> Ops and 'Server' :> Ops)
Full name: Script.Dual`2.Offer
--------------------
type Offer<'Left,'Right (requires 'Left :> Ops and 'Right :> Ops)> =
inherit Ops
Full name: Script.Offer<_,_>
val eps : Dual<Eps,Eps>
Full name: Script.eps
type Dual<'Client,'Server (requires 'Client :> Ops and 'Server :> Ops)> =
private new : unit -> Dual<'Client,'Server>
member Choose : dual:Dual<'Client','Server'> -> Dual<Choose<'Client,'Client'>,Offer<'Server,'Server'>> (requires 'Client' :> Ops and 'Server' :> Ops)
member Offer : dual:Dual<'Client','Server'> -> Dual<Offer<'Client,'Client'>,Choose<'Server,'Server'>> (requires 'Client' :> Ops and 'Server' :> Ops)
member Recv : unit -> Dual<Recv<'T,'Client>,Send<'T,'Server>>
member Send : unit -> Dual<Send<'T,'Client>,Recv<'T,'Server>>
static member Eps : Dual<Eps,Eps>
Full name: Script.Dual<_,_>
Multiple items
union case Msg.Msg: (obj * AsyncReplyChannel<unit>) -> Msg
--------------------
type Msg = | Msg of (obj * AsyncReplyChannel<unit>)
Full name: Script.Msg
type obj = System.Object
Full name: Microsoft.FSharp.Core.obj
type AsyncReplyChannel<'Reply>
member Reply : value:'Reply -> unit
Full name: Microsoft.FSharp.Control.AsyncReplyChannel<_>
type unit = Unit
Full name: Microsoft.FSharp.Core.unit
Multiple items
union case Session.Session: (MailboxProcessor<Msg> -> Async<'T>) -> Session<'S1,'S2,'T>
--------------------
type Session<'S1,'S2,'T> = | Session of (MailboxProcessor<Msg> -> Async<'T>)
Full name: Script.Session<_,_,_>
Multiple items
type MailboxProcessor<'Msg> =
interface IDisposable
new : body:(MailboxProcessor<'Msg> -> Async<unit>) * ?cancellationToken:CancellationToken -> MailboxProcessor<'Msg>
member Post : message:'Msg -> unit
member PostAndAsyncReply : buildMessage:(AsyncReplyChannel<'Reply> -> 'Msg) * ?timeout:int -> Async<'Reply>
member PostAndReply : buildMessage:(AsyncReplyChannel<'Reply> -> 'Msg) * ?timeout:int -> 'Reply
member PostAndTryAsyncReply : buildMessage:(AsyncReplyChannel<'Reply> -> 'Msg) * ?timeout:int -> Async<'Reply option>
member Receive : ?timeout:int -> Async<'Msg>
member Scan : scanner:('Msg -> Async<'T> option) * ?timeout:int -> Async<'T>
member Start : unit -> unit
member TryPostAndReply : buildMessage:(AsyncReplyChannel<'Reply> -> 'Msg) * ?timeout:int -> 'Reply option
...
Full name: Microsoft.FSharp.Control.MailboxProcessor<_>
--------------------
new : body:(MailboxProcessor<'Msg> -> Async<unit>) * ?cancellationToken:System.Threading.CancellationToken -> MailboxProcessor<'Msg>
Multiple items
type Async
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitTask : task:Task<'T> -> Async<'T>
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
static member Ignore : computation:Async<'T> -> Async<unit>
static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T
static member Sleep : millisecondsDueTime:int -> Async<unit>
static member Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions * ?cancellationToken:CancellationToken -> Task<'T>
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartChildAsTask : computation:Async<'T> * ?taskCreationOptions:TaskCreationOptions -> Async<Task<'T>>
static member StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken -> unit
static member SwitchToContext : syncContext:SynchronizationContext -> Async<unit>
static member SwitchToNewThread : unit -> Async<unit>
static member SwitchToThreadPool : unit -> Async<unit>
static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
static member CancellationToken : Async<CancellationToken>
static member DefaultCancellationToken : CancellationToken
Full name: Microsoft.FSharp.Control.Async
--------------------
type Async<'T>
Full name: Microsoft.FSharp.Control.Async<_>
Multiple items
type SessionBuilder =
new : unit -> SessionBuilder
member Bind : session:Session<'S1,'S2,'T> * f:('T -> Session<'S2,'S3,'R>) -> Session<'S1,'S3,'R>
member Return : value:'T -> Session<'S,'S,'T>
Full name: Script.SessionBuilder
--------------------
new : unit -> SessionBuilder
val self : SessionBuilder
member SessionBuilder.Return : value:'T -> Session<'S,'S,'T>
Full name: Script.SessionBuilder.Return
val value : 'T
val async : AsyncBuilder
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
member SessionBuilder.Bind : session:Session<'S1,'S2,'T> * f:('T -> Session<'S2,'S3,'R>) -> Session<'S1,'S3,'R>
Full name: Script.SessionBuilder.Bind
val session : Session<'S1,'S2,'T>
val f : ('T -> Session<'S2,'S3,'R>)
val mailBox : MailboxProcessor<Msg>
val r : (MailboxProcessor<Msg> -> Async<'T>)
val x : 'T
val r' : (MailboxProcessor<Msg> -> Async<'R>)
val session : SessionBuilder
Full name: Script.session
val send : value:'T -> Session<Send<'T,'Rest>,'Rest,unit> (requires 'Rest :> Ops)
Full name: Script.send
member MailboxProcessor.PostAndAsyncReply : buildMessage:(AsyncReplyChannel<'Reply> -> 'Msg) * ?timeout:int -> Async<'Reply>
val reply : AsyncReplyChannel<unit>
val recv : unit -> Session<Recv<'T,'Rest>,'Rest,'T> (requires 'Rest :> Ops)
Full name: Script.recv
val value : obj
member MailboxProcessor.Receive : ?timeout:int -> Async<'Msg>
member AsyncReplyChannel.Reply : value:'Reply -> unit
val sel1 : unit -> Session<Choose<'First,#Ops>,'First,unit> (requires 'First :> Ops)
Full name: Script.sel1
val sel2 : unit -> Session<Choose<#Ops,'Second>,'Second,unit> (requires 'Second :> Ops)
Full name: Script.sel2
val cases : left:Session<'First,'Rest,'T> -> right:Session<'Second,'Rest,'T> -> Session<Offer<'First,'Second>,'Rest,'T> (requires 'First :> Ops and 'Second :> Ops)
Full name: Script.cases
val left : Session<#Ops,'Rest,'T>
val right : Session<#Ops,'Rest,'T>
type bool = System.Boolean
Full name: Microsoft.FSharp.Core.bool
val run : dual:Dual<'Client,'Server> -> client:Session<'Client,Eps,'T> -> server:Session<'Server,Eps,unit> -> 'T (requires 'Client :> Ops and 'Server :> Ops)
Full name: Script.run
val client : Session<#Ops,Eps,'T>
val server : Session<#Ops,Eps,unit>
static member MailboxProcessor.Start : body:(MailboxProcessor<'Msg> -> Async<unit>) * ?cancellationToken:System.Threading.CancellationToken -> MailboxProcessor<'Msg>
val r : (MailboxProcessor<Msg> -> Async<unit>)
val r' : (MailboxProcessor<Msg> -> Async<'T>)
val result : 'T []
module Unchecked
from Microsoft.FSharp.Core.Operators
val defaultof<'T> : 'T
Full name: Microsoft.FSharp.Core.Operators.Unchecked.defaultof
static member Async.Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member Async.RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:System.Threading.CancellationToken -> 'T
val addDual : Dual<Send<int,Send<int,Recv<int,Eps>>>,Recv<int,Recv<int,Send<int,Eps>>>>
Full name: Script.addDual
member Dual.Recv : unit -> Dual<Recv<'T,'Client>,Send<'T,'Server>>
Multiple items
val int : value:'T -> int (requires member op_Explicit)
Full name: Microsoft.FSharp.Core.Operators.int
--------------------
type int = int32
Full name: Microsoft.FSharp.Core.int
--------------------
type int<'Measure> = int
Full name: Microsoft.FSharp.Core.int<_>
val stringToIntDual : Dual<Send<string,Recv<int,Eps>>,Recv<string,Send<int,Eps>>>
Full name: Script.stringToIntDual
Multiple items
val string : value:'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = System.String
Full name: Microsoft.FSharp.Core.string
val chooseDual : Dual<Choose<Send<int,Send<int,Recv<int,Eps>>>,Send<string,Recv<int,Eps>>>,Offer<Recv<int,Recv<int,Send<int,Eps>>>,Recv<string,Send<int,Eps>>>>
Full name: Script.chooseDual
member Dual.Choose : dual:Dual<'Client','Server'> -> Dual<Choose<'Client,'Client'>,Offer<'Server,'Server'>> (requires 'Client' :> Ops and 'Server' :> Ops)
type AddSessionClient = Send<int,Send<int,Recv<int,Eps>>>
Full name: Script.AddSessionClient
val clientAdd : Session<AddSessionClient,Eps,int>
Full name: Script.clientAdd
val result : int
type AddSessionServer = Recv<int,Recv<int,Send<int,Eps>>>
Full name: Script.AddSessionServer
val serverAdd : Session<AddSessionServer,Eps,unit>
Full name: Script.serverAdd
val first : int
val second : int
type StringToIntSessionClient = Send<string,Recv<int,Eps>>
Full name: Script.StringToIntSessionClient
val clientStringToInt : Session<StringToIntSessionClient,Eps,int>
Full name: Script.clientStringToInt
type StringToIntServer = Recv<string,Send<int,Eps>>
Full name: Script.StringToIntServer
val serverStringToInt : Session<StringToIntServer,Eps,unit>
Full name: Script.serverStringToInt
val value : string
namespace System
type Int32 =
struct
member CompareTo : value:obj -> int + 1 overload
member Equals : obj:obj -> bool + 1 overload
member GetHashCode : unit -> int
member GetTypeCode : unit -> TypeCode
member ToString : unit -> string + 3 overloads
static val MaxValue : int
static val MinValue : int
static member Parse : s:string -> int + 3 overloads
static member TryParse : s:string * result:int -> bool + 1 overload
end
Full name: System.Int32
System.Int32.Parse(s: string) : int
System.Int32.Parse(s: string, provider: System.IFormatProvider) : int
System.Int32.Parse(s: string, style: System.Globalization.NumberStyles) : int
System.Int32.Parse(s: string, style: System.Globalization.NumberStyles, provider: System.IFormatProvider) : int
val client : input:int -> Session<Choose<AddSessionClient,StringToIntSessionClient>,Eps,int>
Full name: Script.client
val input : int
val value : int
val server : Session<Offer<AddSessionServer,StringToIntServer>,Eps,unit>
Full name: Script.server
More information