git.iko.soy/distributed-process
on master help sign in
244 lines 7.4 kB history raw
1{-# LANGUAGE CPP #-}
2-- | /Towards Haskell in the Cloud/ (Epstein et al., Haskell Symposium 2011)
3-- proposes a new type construct called 'static' that characterizes values that
4-- are known statically. Cloud Haskell uses the
5-- 'Control.Distributed.Static.Static' implementation from
6-- "Control.Distributed.Static". That module comes with its own extensive
7-- documentation, which you should read if you want to know the details. Here
8-- we explain the Template Haskell support only.
9--
10-- [Static values]
11--
12-- Given a top-level (possibly polymorphic, but unqualified) definition
13--
14-- > f :: forall a1 .. an. T
15-- > f = ...
16--
17-- you can use a Template Haskell splice to create a static version of 'f':
18--
19-- > $(mkStatic 'f) :: forall a1 .. an. (Typeable a1, .., Typeable an) => Static T
20--
21-- Every module that you write that contains calls to 'mkStatic' needs to
22-- have a call to 'remotable':
23--
24-- > remotable [ 'f, 'g, ... ]
25--
26-- where you must pass every function (or other value) that you pass as an
27-- argument to 'mkStatic'. The call to 'remotable' will create a definition
28--
29-- > __remoteTable :: RemoteTable -> RemoteTable
30--
31-- which can be used to construct the 'RemoteTable' used to initialize
32-- Cloud Haskell. You should have (at most) one call to 'remotable' per module,
33-- and compose all created functions when initializing Cloud Haskell:
34--
35-- > let rtable :: RemoteTable
36-- > rtable = M1.__remoteTable
37-- > . M2.__remoteTable
38-- > . ...
39-- > . Mn.__remoteTable
40-- > $ initRemoteTable
41--
42-- NOTE: If you get a type error from ghc along these lines
43--
44-- > The exact Name `a_a30k' is not in scope
45-- > Probable cause: you used a unique name (NameU) in Template Haskell but did not bind it
46--
47-- then you need to enable the @ScopedTypeVariables@ language extension.
48--
49-- [Static serialization dictionaries]
50--
51-- Some Cloud Haskell primitives require static serialization dictionaries (**):
52--
53-- > call :: Serializable a => Static (SerializableDict a) -> NodeId -> Closure (Process a) -> Process a
54--
55-- Given some serializable type 'T' you can define
56--
57-- > sdictT :: SerializableDict T
58-- > sdictT = SerializableDict
59--
60-- and then have
61--
62-- > $(mkStatic 'sdictT) :: Static (SerializableDict T)
63--
64-- However, since these dictionaries are so frequently required Cloud Haskell
65-- provides special support for them. When you call 'remotable' on a
66-- /monomorphic/ function @f :: T1 -> T2@
67--
68-- > remotable ['f]
69--
70-- then a serialization dictionary is automatically created for you, which you
71-- can access with
72--
73-- > $(functionSDict 'f) :: Static (SerializableDict T1)
74--
75-- In addition, if @f :: T1 -> Process T2@, then a second dictionary is created
76--
77-- > $(functionTDict 'f) :: Static (SerializableDict T2)
78--
79-- [Closures]
80--
81-- Suppose you have a process
82--
83-- > isPrime :: Integer -> Process Bool
84--
85-- Then
86--
87-- > $(mkClosure 'isPrime) :: Integer -> Closure (Process Bool)
88--
89-- which you can then 'call', for example, to have a remote node check if
90-- a number is prime.
91--
92-- In general, if you have a /monomorphic/ function
93--
94-- > f :: T1 -> T2
95--
96-- then
97--
98-- > $(mkClosure 'f) :: T1 -> Closure T2
99--
100-- provided that 'T1' is serializable (*) (remember to pass 'f' to 'remotable').
101--
102-- (You can also create closures manually--see the documentation of
103-- "Control.Distributed.Static" for examples.)
104--
105-- [Example]
106--
107-- Here is a small self-contained example that uses closures and serialization
108-- dictionaries. It makes use of the Control.Distributed.Process.SimpleLocalnet
109-- Cloud Haskell backend.
110--
111-- > {-# LANGUAGE TemplateHaskell #-}
112-- > import System.Environment (getArgs)
113-- > import Control.Distributed.Process
114-- > import Control.Distributed.Process.Closure
115-- > import Control.Distributed.Process.Backend.SimpleLocalnet
116-- > import Control.Distributed.Process.Node (initRemoteTable)
117-- >
118-- > isPrime :: Integer -> Process Bool
119-- > isPrime n = return . (n `elem`) . takeWhile (<= n) . sieve $ [2..]
120-- > where
121-- > sieve :: [Integer] -> [Integer]
122-- > sieve (p : xs) = p : sieve [x | x <- xs, x `mod` p > 0]
123-- >
124-- > remotable ['isPrime]
125-- >
126-- > master :: [NodeId] -> Process ()
127-- > master [] = liftIO $ putStrLn "no slaves"
128-- > master (slave:_) = do
129-- > isPrime79 <- call $(functionTDict 'isPrime) slave ($(mkClosure 'isPrime) (79 :: Integer))
130-- > liftIO $ print isPrime79
131-- >
132-- > main :: IO ()
133-- > main = do
134-- > args <- getArgs
135-- > case args of
136-- > ["master", host, port] -> do
137-- > backend <- initializeBackend host port rtable
138-- > startMaster backend master
139-- > ["slave", host, port] -> do
140-- > backend <- initializeBackend host port rtable
141-- > startSlave backend
142-- > where
143-- > rtable :: RemoteTable
144-- > rtable = __remoteTable initRemoteTable
145--
146-- [Notes]
147--
148-- (*) If 'T1' is not serializable you will get a type error in the generated
149-- code. Unfortunately, the Template Haskell infrastructure cannot check
150-- a priori if 'T1' is serializable or not due to a bug in the Template
151-- Haskell libraries (<http://hackage.haskell.org/trac/ghc/ticket/7066>)
152--
153-- (**) Even though 'call' is passed an explicit serialization
154-- dictionary, we still need the 'Serializable' constraint because
155-- 'Static' is not the /true/ static. If it was, we could 'unstatic'
156-- the dictionary and pattern match on it to bring the 'Typeable'
157-- instance into scope, but unless proper 'static' support is added to
158-- ghc we need both the type class argument and the explicit dictionary.
159module Control.Distributed.Process.Closure
160 ( -- * Serialization dictionaries (and their static versions)
161 SerializableDict(..)
162 , staticDecode
163 , sdictUnit
164 , sdictProcessId
165 , sdictSendPort
166 , sdictStatic
167 , sdictClosure
168 -- * The CP type and associated combinators
169 , CP
170 , idCP
171 , splitCP
172 , returnCP
173 , bindCP
174 , seqCP
175 -- * CP versions of Cloud Haskell primitives
176 , cpLink
177 , cpUnlink
178 , cpRelay
179 , cpSend
180 , cpExpect
181 , cpNewChan
182 -- * Working with static values and closures (without Template Haskell)
183 , RemoteRegister
184 , MkTDict(..)
185 , mkStaticVal
186 , mkClosureValSingle
187 , mkClosureVal
188 , call'
189#ifdef TemplateHaskellSupport
190 -- * Template Haskell support for creating static values and closures
191 , remotable
192 , remotableDecl
193 , mkStatic
194 , mkClosure
195 , mkStaticClosure
196 , functionSDict
197 , functionTDict
198#endif
199 ) where
201import Control.Distributed.Process.Serializable (SerializableDict(..))
202import Control.Distributed.Process.Internal.Closure.BuiltIn
203 ( -- Static dictionaries and associated operations
204 staticDecode
205 , sdictUnit
206 , sdictProcessId
207 , sdictSendPort
208 , sdictStatic
209 , sdictClosure
210 -- The CP type and associated combinators
211 , CP
212 , idCP
213 , splitCP
214 , returnCP
215 , bindCP
216 , seqCP
217 -- CP versions of Cloud Haskell primitives
218 , cpLink
219 , cpUnlink
220 , cpRelay
221 , cpSend
222 , cpExpect
223 , cpNewChan
224 )
225import Control.Distributed.Process.Internal.Closure.Explicit
226 (
227 RemoteRegister
228 , MkTDict(..)
229 , mkStaticVal
230 , mkClosureValSingle
231 , mkClosureVal
232 , call'
233 )
234#ifdef TemplateHaskellSupport
235import Control.Distributed.Process.Internal.Closure.TH
236 ( remotable
237 , remotableDecl
238 , mkStatic
239 , functionSDict
240 , functionTDict
241 , mkClosure
242 , mkStaticClosure
243 )
244#endif