@@ -1,29 +1,26 @@
1−-- | Implementation of 'Closure' that works around the absence of 'static'.
1+-- | Static values and Closures
3−-- [User-defined monomorphic closures]
3+-- [Static values]
5−-- Suppose we have a monomorphic function
5+-- /Towards Haskell in the Cloud/ (Epstein et al., Haskell Symposium 2011)
6+-- proposes a new type construct called 'static' that characterizes values that
7+-- are known statically. There is no support for 'static' in ghc yet, however,
8+-- so we emulate it using Template Haskell. Given a top-level definition
7−-- > addInt :: Int -> Int -> Int
10+-- > f :: forall a1 .. an. T
8−-- > addInt x y = x + y
11+-- > f = ...
10−-- Then the Template Haskell splice
13+-- you can use a Template Haskell splice to create a static version of 'f':
12−-- > remotable ['addInt]
14−-- creates a function
15+-- > $(mkStatic 'f) :: forall a1 .. an. Static T
16−-- > $(mkClosure 'addInt) :: Int -> Closure (Int -> Int)
18−-- which can be used to partially apply 'addInt' and turn it into a 'Closure',
17+-- Every module that you write that contains calls to 'mkStatic' needs to
19−-- which can be sent across the network. Closures can be deserialized with
18+-- have a call to 'remotable':
21−-- > unClosure :: Typeable a => Closure a -> Process a
23−-- In general, given a monomorphic function @f :: T1 -> T2@ the corresponding
20+-- > remotable [ 'f, 'g, ... ]
24−-- function @$(mkClosure 'f)@ will have type @T1 -> Closure T2@.
22+-- where you must pass every function (or other value) that you pass as an
26−-- The call to 'remotable' will also generate a function
23+-- argument to 'mkStatic'. The call to 'remotable' will create a definition
28-- > __remoteTable :: RemoteTable -> RemoteTable
25-- > __remoteTable :: RemoteTable -> RemoteTable
@@ -31,62 +28,124 @@
31-- Cloud Haskell. You should have (at most) one call to 'remotable' per module,
28-- Cloud Haskell. You should have (at most) one call to 'remotable' per module,
32-- and compose all created functions when initializing Cloud Haskell:
29-- and compose all created functions when initializing Cloud Haskell:
31+-- > let rtable :: RemoteTable
34−-- > let rtable = M1.__remoteTable
32+-- > rtable = M1.__remoteTable
35-- > . M2.__remoteTable
33-- > . M2.__remoteTable
37-- > . Mn.__remoteTable
35-- > . Mn.__remoteTable
38-- > $ initRemoteTable
36-- > $ initRemoteTable
40−-- See Section 6, /Faking It/, of /Towards Haskell in the Cloud/ for more info.
38+-- [Composing static values]
40+-- We generalize the notion of 'static' as described in the paper, and also
43+-- > staticApply :: Static (a -> b) -> Static a -> Static b
45+-- This makes it possible to define a rich set of combinators on 'static'
46+-- values, a number of which are provided in this module.
50+-- Suppose you have a process
52+-- > factorial :: Int -> Process Int
42−-- [User-defined polymorphic closures]
54+-- Then you can use the supplied Template Haskell function 'mkClosure' to define
44−-- Suppose we have a polymorphic function
56+-- > factorialClosure :: Int -> Closure (Process Int)
57+-- > factorialClosure = $(mkClosure 'factorial)
46−-- > first :: forall a b. (a, b) -> a
59+-- You can then pass 'factorialClosure n' to 'spawn', for example, to have a
47−-- > first (x, _) = x
60+-- remote node compute a factorial number.
49−-- Then the Template Haskell splice
62+-- In general, if you have a /monomorphic/ function
51−-- > remotable ['first]
64+-- > f :: T1 -> T2
53−-- creates a function
66+-- then
55−-- > $(mkClosure 'first) :: forall a b. (Typeable a, Typeable b)
56−-- > => Closure (a -> b -> a)
68+-- > $(mkClosure 'f) :: T1 -> Closure T2
58−-- In general, given a polymorphic function @f :: forall a1 .. an. T@ the
70+-- provided that 'T1' is serializable (*).
59−-- corresponding function @$(mkClosure 'f)@ will have type
60−-- @forall a1 .. an. (Typeable a1, .., Typeable an) => Closure T@.
62−-- [Built-in closures]
72+-- [Creating closures manually]
64−-- We offer a number of standard commonly useful closures.
74+-- You don't /need/ to use 'mkClosure', however. Closures are defined exactly
75+-- as described in /Towards Haskell in the Cloud/:
77+-- > data Closure a = Closure (Static (ByteString -> a)) ByteString
79+-- The splice @$(mkClosure 'factorial)@ above expands to (prettified a bit):
81+-- > factorialClosure :: Int -> Closure (Process Int)
82+-- > factorialClosure n = Closure decoder (encode n)
84+-- > decoder :: Static (ByteString -> Process Int)
85+-- > decoder = $(mkStatic 'factorial)
87+-- > staticDecode $(functionSDict 'factorial)
66−-- [Closure combinators]
89+-- 'mkStatic' we have already seen:
68−-- Closures combinators allow to create closures from other closures. For
91+-- > $(mkStatic 'factorial) :: Static (Int -> Process Int)
69−-- example, 'spawnSupervised' is defined as follows:
71−-- > spawnSupervised :: NodeId
93+-- 'staticCompose' is function composition on static functions. 'staticDecode'
72−-- > -> Closure (Process ())
94+-- has type (**)
73−-- > -> Process (ProcessId, MonitorRef)
74−-- > spawnSupervised nid proc = do
76−-- > them <- spawn nid (linkClosure us `cpSeq` proc)
77−-- > ref <- monitor them
78−-- > return (them, ref)
80−-- [Serializable Dictionaries]
96+-- > staticDecode :: Typeable a
97+-- > => Static (SerializableDict a) -> Static (ByteString -> a)
99+-- and gives you a static decoder, given a static Serializable dictionary.
100+-- 'SerializableDict' is a reified type class dictionary, and defined simply as
102+-- > data SerializableDict a where
103+-- > SerializableDict :: Serializable a => SerializableDict a
82−-- Some functions (such as 'sendClosure' or 'returnClosure') require an
105+-- That means that for any serialziable type 'T', you can define
83−-- explicit (reified) serializable dictionary. To create such a dictionary do
85−-- > sdictInt :: SerializableDict Int
107+-- > sdictForMyType :: SerializableDict T
86−-- > sdictInt = SerializableDict
108+-- > sdictForMyType = SerializableDict
112+-- > $(mkStatic 'sdictForMyType) :: Static (SerializableDict T)
114+-- to obtain a static serializable dictionary for 'T' (make sure to pass
115+-- 'sdictForMyType' to 'remotable').
117+-- However, since these serialization dictionaries are so frequently required,
118+-- when you call 'remotable' on a monomorphic function @f : T1 -> T2@
88−-- and then pass @'sdictInt@ to 'remotable'. This will fail if the
120+-- > remotable ['f]
89−-- type is not serializable.
121+--
122+-- then a serialization dictionary is automatically created for you, which you
125+-- > $(functionDict 'f) :: Static (SerializableDict T1)
127+-- This is the dictionary that 'mkClosure' uses.
129+-- [Combinators on Closures]
131+-- Support for 'staticApply' (described above) also means that we can define
132+-- combinators on Closures, and we provide a number of them in this module,
133+-- the most important of which is 'cpBind'. Have a look at the implementation
134+-- of 'Control.Distributed.Process.call' for an example use.
138+-- (*) If 'T1' is not serializable you will get a type error in the generated
139+-- code. Unfortunately, the Template Haskell infrastructure cannot check
140+-- a priori if 'T1' is serializable or not due to a bug in the Template
141+-- Haskell libraries (<http://hackage.haskell.org/trac/ghc/ticket/7066>)
143+-- (**) Even though 'staticDecode' is passed an explicit serialization
144+-- dictionary, we still need the 'Typeable' constraint because
145+-- 'Static' is not the /true/ static. If it was, we could 'unstatic'
146+-- the dictionary and pattern match on it to bring the 'Typeable'
147+-- instance into scope, but unless proper 'static' support is added to
148+-- ghc we need both the type class argument and the explicit dictionary.
90module Control.Distributed.Process.Closure
149module Control.Distributed.Process.Closure
91 ( -- * User-defined closures
150 ( -- * User-defined closures
92 remotable
151 remotable
@@ -98,6 +157,9 @@ module Control.Distributed.Process.Closure
98 , staticDuplicate
157 , staticDuplicate
99 -- * Static functionals
158 -- * Static functionals
100 , staticConst
159 , staticConst
101 , staticCompose
163 , staticCompose
102 , staticFirst
164 , staticFirst
103 , staticSecond
165 , staticSecond
@@ -108,6 +170,22 @@ module Control.Distributed.Process.Closure
108 , staticDecode
170 , staticDecode
109 , staticClosure
171 , staticClosure
110 , toClosure
172 , toClosure
173+ -- * Serialization dictionaries (and their static versions)
174+ , SerializableDict(..)
178+ -- * Definition of CP and the generalized arrow combinators
111 -- * Closure versions of CH primitives
189 -- * Closure versions of CH primitives
113 , cpUnlink
191 , cpUnlink
@@ -118,11 +196,6 @@ module Control.Distributed.Process.Closure
118 , cpReturn
196 , cpReturn
121− -- * Serialization dictionaries
122− , SerializableDict(..)
128import Control.Distributed.Process.Internal.Types
201import Control.Distributed.Process.Internal.Types
@@ -138,11 +211,14 @@ import Control.Distributed.Process.Internal.Closure.TH
138import Control.Distributed.Process.Internal.Closure.Static
211import Control.Distributed.Process.Internal.Closure.Static
139 ( -- Static functionals
212 ( -- Static functionals
140 staticConst
213 staticConst
141 , staticCompose
217 , staticCompose
142 , staticFirst
218 , staticFirst
143 , staticSecond
219 , staticSecond
144 , staticSplit
220 , staticSplit
145− -- Constants
221+ -- Static constants
146 , staticUnit
222 , staticUnit
147 -- Creating closures
223 -- Creating closures
148 , staticDecode
224 , staticDecode
@@ -155,8 +231,19 @@ import Control.Distributed.Process.Internal.Closure.Static
156import Control.Distributed.Process.Internal.Closure.MkClosure (mkClosure)
232import Control.Distributed.Process.Internal.Closure.MkClosure (mkClosure)
157import Control.Distributed.Process.Internal.Closure.CP
233import Control.Distributed.Process.Internal.Closure.CP
234+ ( -- Definition of CP and the generalized arrow combinators
158− ( -- Closure versions of CH primitives
245+ -- Closure versions of CH primitives
160 , cpUnlink
247 , cpUnlink
162 , cpExpect
249 , cpExpect