distributed-process-0.2.1.3: src/Control/Distributed/Process/Closure.hs
-- | Static values and Closures
--
-- [Static values]
--
-- /Towards Haskell in the Cloud/ (Epstein et al., Haskell Symposium 2011)
-- proposes a new type construct called 'static' that characterizes values that
-- are known statically. There is no support for 'static' in ghc yet, however,
-- so we emulate it using Template Haskell. Given a top-level definition
--
-- > f :: forall a1 .. an. T
-- > f = ...
--
-- you can use a Template Haskell splice to create a static version of 'f':
--
-- > $(mkStatic 'f) :: forall a1 .. an. Static T
--
-- Every module that you write that contains calls to 'mkStatic' needs to
-- have a call to 'remotable':
--
-- > remotable [ 'f, 'g, ... ]
--
-- where you must pass every function (or other value) that you pass as an
-- argument to 'mkStatic'. The call to 'remotable' will create a definition
--
-- > __remoteTable :: RemoteTable -> RemoteTable
--
-- which can be used to construct the 'RemoteTable' used to initialize
-- Cloud Haskell. You should have (at most) one call to 'remotable' per module,
-- and compose all created functions when initializing Cloud Haskell:
--
-- > let rtable :: RemoteTable
-- > rtable = M1.__remoteTable
-- > . M2.__remoteTable
-- > . ...
-- > . Mn.__remoteTable
-- > $ initRemoteTable
--
-- [Composing static values]
--
-- We generalize the notion of 'static' as described in the paper, and also
-- provide
--
-- > staticApply :: Static (a -> b) -> Static a -> Static b
--
-- This makes it possible to define a rich set of combinators on 'static'
-- values, a number of which are provided in this module.
--
-- [Closures]
--
-- Suppose you have a process
--
-- > factorial :: Int -> Process Int
--
-- Then you can use the supplied Template Haskell function 'mkClosure' to define
--
-- > factorialClosure :: Int -> Closure (Process Int)
-- > factorialClosure = $(mkClosure 'factorial)
--
-- You can then pass 'factorialClosure n' to 'spawn', for example, to have a
-- remote node compute a factorial number.
--
-- In general, if you have a /monomorphic/ function
--
-- > f :: T1 -> T2
--
-- then
--
-- > $(mkClosure 'f) :: T1 -> Closure T2
--
-- provided that 'T1' is serializable (*).
--
-- [Creating closures manually]
--
-- You don't /need/ to use 'mkClosure', however. Closures are defined exactly
-- as described in /Towards Haskell in the Cloud/:
--
-- > data Closure a = Closure (Static (ByteString -> a)) ByteString
--
-- The splice @$(mkClosure 'factorial)@ above expands to (prettified a bit):
--
-- > factorialClosure :: Int -> Closure (Process Int)
-- > factorialClosure n = Closure decoder (encode n)
-- > where
-- > decoder :: Static (ByteString -> Process Int)
-- > decoder = $(mkStatic 'factorial)
-- > `staticCompose`
-- > staticDecode $(functionSDict 'factorial)
--
-- 'mkStatic' we have already seen:
--
-- > $(mkStatic 'factorial) :: Static (Int -> Process Int)
--
-- 'staticCompose' is function composition on static functions. 'staticDecode'
-- has type (**)
--
-- > staticDecode :: Typeable a
-- > => Static (SerializableDict a) -> Static (ByteString -> a)
--
-- and gives you a static decoder, given a static Serializable dictionary.
-- 'SerializableDict' is a reified type class dictionary, and defined simply as
--
-- > data SerializableDict a where
-- > SerializableDict :: Serializable a => SerializableDict a
--
-- That means that for any serialziable type 'T', you can define
--
-- > sdictForMyType :: SerializableDict T
-- > sdictForMyType = SerializableDict
--
-- and then use
--
-- > $(mkStatic 'sdictForMyType) :: Static (SerializableDict T)
--
-- to obtain a static serializable dictionary for 'T' (make sure to pass
-- 'sdictForMyType' to 'remotable').
--
-- However, since these serialization dictionaries are so frequently required,
-- when you call 'remotable' on a monomorphic function @f : T1 -> T2@
--
-- > remotable ['f]
--
-- then a serialization dictionary is automatically created for you, which you
-- can access with
--
-- > $(functionSDict 'f) :: Static (SerializableDict T1)
--
-- This is the dictionary that 'mkClosure' uses.
--
-- [Combinators on Closures]
--
-- Support for 'staticApply' (described above) also means that we can define
-- combinators on Closures, and we provide a number of them in this module,
-- the most important of which is 'cpBind'. Have a look at the implementation
-- of 'Control.Distributed.Process.call' for an example use.
--
-- [Notes]
--
-- (*) If 'T1' is not serializable you will get a type error in the generated
-- code. Unfortunately, the Template Haskell infrastructure cannot check
-- a priori if 'T1' is serializable or not due to a bug in the Template
-- Haskell libraries (<http://hackage.haskell.org/trac/ghc/ticket/7066>)
--
-- (**) Even though 'staticDecode' is passed an explicit serialization
-- dictionary, we still need the 'Typeable' constraint because
-- 'Static' is not the /true/ static. If it was, we could 'unstatic'
-- the dictionary and pattern match on it to bring the 'Typeable'
-- instance into scope, but unless proper 'static' support is added to
-- ghc we need both the type class argument and the explicit dictionary.
module Control.Distributed.Process.Closure
( -- * User-defined closures
remotable
, mkStatic
, mkClosure
, functionSDict
-- * Primitive operations on static values
, staticApply
, staticDuplicate
-- * Static functionals
, staticConst
, staticFlip
, staticFst
, staticSnd
, staticCompose
, staticFirst
, staticSecond
, staticSplit
-- * Static constants
, staticUnit
-- * Creating closures
, staticDecode
, staticClosure
, toClosure
-- * Serialization dictionaries (and their static versions)
, SerializableDict(..)
, sdictUnit
, sdictProcessId
, sdictSendPort
-- * Definition of CP and the generalized arrow combinators
, CP
, cpIntro
, cpElim
, cpId
, cpComp
, cpFirst
, cpSecond
, cpSplit
, cpCancelL
, cpCancelR
-- * Closure versions of CH primitives
, cpLink
, cpUnlink
, cpSend
, cpExpect
, cpNewChan
-- * @Closure (Process a)@ as a not-quite-monad
, cpReturn
, cpBind
, cpSeq
) where
import Control.Distributed.Process.Internal.Types
( SerializableDict(..)
, staticApply
, staticDuplicate
)
import Control.Distributed.Process.Internal.Closure.TH
( remotable
, mkStatic
, functionSDict
)
import Control.Distributed.Process.Internal.Closure.Static
( -- Static functionals
staticConst
, staticFlip
, staticFst
, staticSnd
, staticCompose
, staticFirst
, staticSecond
, staticSplit
-- Static constants
, staticUnit
-- Creating closures
, staticDecode
, staticClosure
, toClosure
-- Serialization dictionaries (and their static versions)
, sdictUnit
, sdictProcessId
, sdictSendPort
)
import Control.Distributed.Process.Internal.Closure.MkClosure (mkClosure)
import Control.Distributed.Process.Internal.Closure.CP
( -- Definition of CP and the generalized arrow combinators
CP
, cpIntro
, cpElim
, cpId
, cpComp
, cpFirst
, cpSecond
, cpSplit
, cpCancelL
, cpCancelR
-- Closure versions of CH primitives
, cpLink
, cpUnlink
, cpSend
, cpExpect
, cpNewChan
-- @Closure (Process a)@ as a not-quite-monad
, cpReturn
, cpBind
, cpSeq
)