This file defines effectful operations which encode the core operations
of the (session type) pi-calculus.
>
> module Control.Effect.Sessions.Operations where
> import Control.Effect.Sessions.Process
> import Data.Type.FiniteMap
> import Unsafe.Coerce
> import Control.Concurrent ( threadDelay )
> import qualified Control.Concurrent.Chan as C
> import qualified Control.Concurrent as Conc
> import Control.Monad.STM
> import Control.Concurrent.STM.TMVar
> import Control.Effect (Subeffect(..))
>
> run :: Process '[] a -> IO a
> run = getProcess
>
> liftIO :: IO a -> Process '[] a
> liftIO = Process
>
> print :: Show a => a -> Process '[] ()
> print = liftIO . (Prelude.print)
>
> putStrLn = liftIO . Prelude.putStrLn
The simplest operations, send and receive of primitive values,
take a named channel 'Chan c' and return a 'Process' computation
indexed by the session environment '[c :-> S]' where 'S' is either a
send or receive action (terminated by 'End').
>
> send :: Chan c -> t -> Process '[c :-> t :! End] ()
> send (MkChan c) t = Process $ C.writeChan (unsafeCoerce c) t
>
> recv :: Chan c -> Process '[c :-> t :? End] t
> recv (MkChan c) = Process $ C.readChan (unsafeCoerce c)
The 'new' combinator models $\nu$, which takes
a function mapping from a pair of two channels names
'Ch c' and 'Op C' to a session with behaviour 's', and creates
a session where any mention to 'Ch c' or 'Op c' is removed:
>
> new :: (Duality env c)
> => ((Chan (Ch c), Chan (Op c)) -> Process env t)
> -> Process (env :\ (Op c) :\ (Ch c)) t
> new f = Process $ C.newChan >>= (\c -> getProcess $ f (MkChan c, MkChan c))
>
> par :: (BalancedPar env env') =>
> Process env () -> Process env' () -> Process (DisjointUnion env env') ()
> par (Process x) (Process y) = Process $ do res <- newEmptyTMVarIO
> res' <- newEmptyTMVarIO
> Conc.forkIO $ (x >>= (atomically . (putTMVar res)))
> Conc.forkIO $ (y >>= (atomically . (putTMVar res')))
> () <- atomically $ do { takeTMVar res }
> () <- atomically $ do { takeTMVar res' }
> return ()
>
> type family AllBal (env :: [Map Name Session]) :: [Map Name Session] where
> AllBal '[] = '[]
> AllBal ((c :-> s) ': env) = (c :-> Bal s) ': (AllBal env)
>
> rsend :: Chan c -> Chan d -> Process '[c :-> (Delg s) :*! End, d :-> Bal s] ()
> rsend (MkChan c) t = Process $ C.writeChan (unsafeCoerce c) t
Channels can then be sent and received with the 'chSend' and 'chRecv' primitives:
>
> chSend :: Chan c -> Chan d -> Process '[c :-> (Delg s) :! End, d :-> Bal s] ()
> chSend (MkChan c) t = Process $ C.writeChan (unsafeCoerce c) t
>
chRecv :: Chan c -> (Chan d -> Process env a) ->
Process (UnionSeq '[c :-> (Delg (env :@ d)) :? (env :@ c)] (env :\ d)) a
chRecv (MkChan c) f = Process $ C.readChan (unsafeCoerce c) >>=
(getProcess . f . unsafeCoerce)
> chRecv :: Chan c -> Process '[c :-> (Delg (env :@ d)) :? End]
> ((Chan d -> Process env a) -> Process (env :\ d) a)
> chRecv (MkChan c) = Process $ return $
> \f -> let y = (C.readChan (unsafeCoerce c) >>=
> (getProcess . f . unsafeCoerce))
> in Process $ y
Given a channel 'c', and a computation which binds channel 'd' to produces behaviour
'c', then this is provided by receiving 'd' over 'c'. Thus the resulting computation
is the union of 'c' mapping to the session type of 'd' in the session environment
's', composed with the 's' but with 'd' deleted (removed).
------------------------------------------------------
Subeffecting instances for the least-upper bound :+ operator and for extending
an environment with a closed session channel, i.e. with c :-> End.
> instance Subeffect Process env env' =>
> Subeffect Process ((c :-> s) ': env) ((c :-> s :+ t) ': env') where
> sub (Process p) = Process p
> instance Subeffect Process env env' =>
> Subeffect Process ((c :-> t) ': env) ((c :-> s :+ t) ': env') where
> sub (Process p) = Process p
> instance Subeffect Process env env where
> sub = id
> instance Subeffect Process env env' =>
> Subeffect Process ((c :-> s) ': env) ((c :-> s) ': env') where
> sub (Process p) = Process p
> instance Subeffect Process env env' =>
> Subeffect Process env ((c :-> End) ': env') where
> sub (Process p) = Process p
Explicit subeffecting operations for subtyping on the left of a conditional
and subtyping on the right of a conditional
>
> subL :: Process '[c :-> s] a -> Process '[c :-> s :+ t] a
> subL = sub
>
> subR :: Process '[c :-> t] a -> Process '[c :-> s :+ t] a
> subR = sub
>
> subEnd :: Chan c -> Process '[] a -> Process '[c :-> End] a
> subEnd _ = sub
instance Subeffect Process '[] '[d :-> s] where
sub (Process p) = Process p
> caseUnion :: Chan c -> Process env a -> Process ((c :-> s) ': env) a
> caseUnion _ (Process p) = Process p
>
> affineFix :: ((a -> Process '[c :-> Star] b)
> -> (a -> Process '[c :-> h] b))
> -> a -> Process '[c :-> ToFix h] b
> affineFix f x = let (Process p) = f (\x' -> let (Process y) = affineFix f x' in Process y) x
> in Process p