git.iko.soy/distributed-process
on c0efe96 help sign in
242 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 , sdictClosure
167 -- * The CP type and associated combinators
168 , CP
169 , idCP
170 , splitCP
171 , returnCP
172 , bindCP
173 , seqCP
174 -- * CP versions of Cloud Haskell primitives
175 , cpLink
176 , cpUnlink
177 , cpRelay
178 , cpSend
179 , cpExpect
180 , cpNewChan
181 -- * Working with static values and closures (without Template Haskell)
182 , RemoteRegister
183 , MkTDict(..)
184 , mkStaticVal
185 , mkClosureValSingle
186 , mkClosureVal
187 , call'
188#ifdef TemplateHaskellSupport
189 -- * Template Haskell support for creating static values and closures
190 , remotable
191 , remotableDecl
192 , mkStatic
193 , mkClosure
194 , mkStaticClosure
195 , functionSDict
196 , functionTDict
197#endif
198 ) where
200import Control.Distributed.Process.Serializable (SerializableDict(..))
201import Control.Distributed.Process.Internal.Closure.BuiltIn
202 ( -- Static dictionaries and associated operations
203 staticDecode
204 , sdictUnit
205 , sdictProcessId
206 , sdictSendPort
207 , sdictClosure
208 -- The CP type and associated combinators
209 , CP
210 , idCP
211 , splitCP
212 , returnCP
213 , bindCP
214 , seqCP
215 -- CP versions of Cloud Haskell primitives
216 , cpLink
217 , cpUnlink
218 , cpRelay
219 , cpSend
220 , cpExpect
221 , cpNewChan
222 )
223import Control.Distributed.Process.Internal.Closure.Explicit
224 (
225 RemoteRegister
226 , MkTDict(..)
227 , mkStaticVal
228 , mkClosureValSingle
229 , mkClosureVal
230 , call'
231 )
232#ifdef TemplateHaskellSupport
233import Control.Distributed.Process.Internal.Closure.TH
234 ( remotable
235 , remotableDecl
236 , mkStatic
237 , functionSDict
238 , functionTDict
239 , mkClosure
240 , mkStaticClosure
241 )
242#endif