Session types are an attempt to describe layer descriptions on network protocols. The idea is that if a client sends a value, the server must be ready to receive it, and vice versa.
So here is a type (using TypeInType ) that describes sessions consisting of a sequence of values ββto send and values ββto receive.
infixr 5 :!, :? data Session = Type :! Session | Type :? Session | E
a :! s a :! s means "send a value of type a and then continue with protocol s ". a :? s a :? s means "get a value of type a and then continue with protocol s ".
So, Session is a (type-level) list of actions. Our monadic calculations will work their way up this list, sending and receiving data as needed. More specifically, evaluating the type Chan sta reduces the remaining work that must be done to satisfy the protocol from s to t . I will build Chan using the indexed free monad that I used in my answer to your other question.
class IFunctor f where imap :: (a -> b) -> fija -> fijb class IFunctor m => IMonad m where ireturn :: a -> miia (>>>=) :: mija -> (a -> mjkb) -> mikb data IFree fija where IReturn :: a -> IFree fiia IFree :: fij (IFree fjka) -> IFree fika instance IFunctor f => IFunctor (IFree f) where imap f (IReturn x) = IReturn (fx) imap f (IFree fx) = IFree (imap (imap f) fx) instance IFunctor f => IMonad (IFree f) where ireturn = IReturn IReturn x >>>= f = fx IFree fx >>>= f = IFree (imap (>>>= f) fx)
Our basic actions in the Chan monad will simply send and receive values.
data ChanF str where Send :: a -> r -> ChanF (a :! s) sr Recv :: (a -> r) -> ChanF (a :? s) sr instance IFunctor ChanF where imap f (Send xr) = Send x (fr) imap f (Recv r) = Recv (fmap fr) send :: a -> Chan (a :! s) s () send x = IFree (Send x (IReturn ())) recv :: Chan (a :? s) sa recv = IFree (Recv IReturn) type Chan = IFree ChanF type Chan' s = Chan s E -- a "complete" Chan
send takes the current session state from a :! s a :! s to s , fulfilling the obligation to send a . Similarly, recv converts the session from a :? s a :? s to s .
Here's the interesting part. When one end of the protocol sends a value, the other end must be ready to receive it, and vice versa. This leads to the idea of ββa double session type:
type family Dual s where Dual (a :! s) = a :? Dual s Dual (a :? s) = a :! Dual s Dual E = E
In a common language, Dual (Dual s) = s will be provable, but alas, Haskell is not complete.
You can connect a pair of channels if their types are dual. (I think you would call it a memory simulation for connecting the client and server.)
connect :: Chan' sa -> Chan' (Dual s) b -> (a, b) connect (IReturn x) (IReturn y) = (x, y) connect (IReturn _) (IFree y) = case y of {} connect (IFree (Send xr)) (IFree (Recv f)) = connect r (fx) connect (IFree (Recv f)) (IFree (Send yr)) = connect (fy) r
For example, a protocol is used here for a server that checks to see if it is greater than 3. 3. The server waits for an Int , sends Bool back, and then completes the calculation.
type MyProtocol = Int :? Bool :! E server :: Chan' MyProtocol () server = do -- using RebindableSyntax x <- recv send (x > 3) client :: Chan' (Dual MyProtocol) Bool client = do send 5 recv
And check this out:
ghci> connect server client ((),True)
Session types are an area of ββactive research. This particular implementation is great for very simple protocols, but you cannot describe protocols where the type of data sent over the wire depends on the state of the protocol. To do this, you need unexpected surprises, dependent types. See this talk from Brady for a quick demonstration of the state of session types.