git.iko.soy/distributed-process
on master help sign in
Focus on 'Static', not 'Closure'

In particular, provide staticApply as primitive, not closureApply. This actually makes *much* more sense, as is evidenced by the fact that resolveClosure is now only a couple of lines, and contains no special treatment of any function at all; moreover, call and co now have the proper type with Static (SerializableDict a) -> ... and we don't use any trickery to define them; in fact, users can define their own similar functions just as easily. This commit represents a signficant rewrite of the entire treatment of closures, up to the point that the unit tests pass again. We don't yet provide support for $mkClosure.

commit55dfe3aea34e88c0c47010c914ff1f46102dd7e9 authorEdsko de Vries <edsko@edsko.net> dateWed 11 Jul 2012, 01:03 (14 years ago) parent634ae01
10 files changed+554−402
distributed-process/src/Control/Distributed/Process.hs+16−14 distributed-process/src/Control/Distributed/Process/Closure.hs+54−70 distributed-process/src/Control/Distributed/Process/Internal/Closure/BuiltIn.hs+11−1 distributed-process/src/Control/Distributed/Process/Internal/Closure/Derived.hs+289−155 distributed-process/src/Control/Distributed/Process/Internal/Closure/Resolution.hs+12−35 distributed-process/src/Control/Distributed/Process/Internal/Closure/TH.hs+45−16 distributed-process/src/Control/Distributed/Process/Internal/Dynamic.hs+25−1 distributed-process/src/Control/Distributed/Process/Internal/Types.hs+17−50 distributed-process/src/Control/Distributed/Process/Node.hs+2−2 distributed-process/tests/TestClosure.hs+83−58
distributed-process/src/Control/Distributed/Process.hs+16−14
@@ -7,6 +7,7 @@
77-- although some of the details are different. The precise message passing
88-- semantics are based on /A unified semantics for future Erlang/ by Hans
99-- Svensson, Lars-Åke Fredlund and Clara Benac Earle.
10+{-# LANGUAGE TemplateHaskell #-}
1011module Control.Distributed.Process
1112 ( -- * Basic types
1213 ProcessId
@@ -116,16 +117,15 @@ import Control.Distributed.Process.Internal.Types
116117 , SendPortId(..)
117118 , WhereIsReply(..)
118119 )
119−import Control.Distributed.Process.Internal.Closure.BuiltIn
120− ( sendClosure
121− , expectClosure
122− )
123120import Control.Distributed.Process.Internal.Closure.Derived
124121 ( cpSeq
125122 , cpBind
126− , linkClosure
127− , unlinkClosure
128− , serializableDictUnit
123+ , cpSend
124+ , cpExpect
125+ , cpLink
126+ , cpUnlink
127+ , sdictUnit
128+ , sdictUnit__static
129129 )
130130import Control.Distributed.Process.Internal.Primitives
131131 ( -- Basic messaging
@@ -179,6 +179,8 @@ import Control.Distributed.Process.Internal.Primitives
179179 , expectTimeout
180180 , spawnAsync
181181 )
182+import Control.Distributed.Process.Serializable (Serializable)
183+import Control.Distributed.Process.Internal.Closure.TH (mkStatic)
182184
183185-- INTERNAL NOTES
184186--
@@ -253,9 +255,9 @@ spawn nid proc = do
253255 -- we call linkNode here, and unlinkNode after, then we might remove a link
254256 -- that was already set up
255257 mRef <- monitorNode nid
256− sRef <- spawnAsync nid $ linkClosure us
257− `cpSeq` expectClosure serializableDictUnit
258− `cpSeq` unlinkClosure us
258+ sRef <- spawnAsync nid $ cpLink us
259+ `cpSeq` cpExpect $(mkStatic 'sdictUnit)
260+ `cpSeq` cpUnlink us
259261 `cpSeq` proc
260262 mPid <- receiveWait
261263 [ matchIf (\(DidSpawn ref _) -> ref == sRef)
@@ -290,10 +292,10 @@ spawnMonitor nid proc = do
290292--
291293-- We monitor the remote process; if it dies before it can send a reply, we die
292294-- too
293−call :: SerializableDict a -> NodeId -> Closure (Process a) -> Process a
294−call sdict@SerializableDict nid proc = do
295+call :: Serializable a => Static (SerializableDict a) -> NodeId -> Closure (Process a) -> Process a
296+call dict nid proc = do
295297 us <- getSelfPid
296− (_, mRef) <- spawnMonitor nid (proc `cpBind` sendClosure sdict us)
298+ (_, mRef) <- spawnMonitor nid (proc `cpBind` cpSend dict us)
297299 -- We are guaranteed to receive the reply before the monitor notification
298300 -- (if a reply is sent at all)
299301 -- NOTE: This might not be true if we switch to unreliable delivery.
@@ -313,7 +315,7 @@ spawnSupervised :: NodeId
313315 -> Process (ProcessId, MonitorRef)
314316spawnSupervised nid proc = do
315317 us <- getSelfPid
316− them <- spawn nid (linkClosure us `cpSeq` proc)
318+ them <- spawn nid (cpLink us `cpSeq` proc)
317319 ref <- monitor them
318320 return (them, ref)
319321
distributed-process/src/Control/Distributed/Process/Closure.hs+54−70
@@ -82,87 +82,71 @@
8282-- Some functions (such as 'sendClosure' or 'returnClosure') require an
8383-- explicit (reified) serializable dictionary. To create such a dictionary do
8484--
85−-- > serializableDictInt :: SerializableDict Int
86−-- > serializableDictInt = SerializableDict
85+-- > sdictInt :: SerializableDict Int
86+-- > sdictInt = SerializableDict
8787--
88−-- and then pass @'serializableDictInt@ to 'remotable'. This will fail if the
88+-- and then pass @'sdictInt@ to 'remotable'. This will fail if the
8989-- type is not serializable.
9090module Control.Distributed.Process.Closure
9191 ( -- * User-defined closures
9292 remotable
93− , mkClosure
94− -- * Process primitives
95− , linkClosure
96− , unlinkClosure
97− , sendClosure
98− , returnClosure
99− , expectClosure
100− -- * Generic closure combinators
101− , closureApply
102− , closureConst
103− , closureUnit
104− -- * Arrow combinators for processes
105− , CP
106− , cpIntro
107− , cpElim
108− , cpId
109− , cpComp
110− , cpFirst
111− , cpSwap
112− , cpSecond
113− , cpPair
114− , cpCopy
115− , cpFanOut
116− , cpLeft
117− , cpMirror
118− , cpRight
119− , cpEither
120− , cpUntag
121− , cpFanIn
122− -- * Derived combinators for processes
93+ , mkStatic
94+ -- * Static functionals
95+ , staticConst
96+ , staticCompose
97+ , staticFirst
98+ , staticSecond
99+ , staticSplit
100+ -- * Creating closures
101+ , staticDecode
102+ , staticClosure
103+ , toClosure
104+ -- * Closure versions of CH primitives
105+ , cpLink
106+ , cpUnlink
107+ , cpSend
108+ , cpExpect
109+ -- * @Closure (Process a)@ as a not-quite-monad
110+ , cpReturn
123111 , cpBind
124112 , cpSeq
125− -- * Serialization dictionaries
113+ -- * Serialization dictionaries (and their static versions)
126114 , SerializableDict(..)
127− , serializableDictUnit
115+ , staticApply
116+ , sdictUnit
117+ , sdictUnit__static
118+ , sdictProcessId
119+ , sdictProcessId__static
128120 ) where
129121
130−import Control.Distributed.Process.Internal.Types (SerializableDict(..))
131−import Control.Distributed.Process.Internal.Closure.TH (remotable, mkClosure)
132−import Control.Distributed.Process.Internal.Closure.BuiltIn
133− ( sendClosure
134− , returnClosure
135− , expectClosure
122+import Control.Distributed.Process.Internal.Types
123+ ( SerializableDict(..)
124+ , staticApply
136125 )
137−import Control.Distributed.Process.Internal.Closure.Derived
138− ( -- Process primitives
139− linkClosure
140− , unlinkClosure
141− -- Generic combinators
142− , closureApply
143− , closureConst
144− , closureUnit
145− -- Arrow combinators for processes
146− , CP
147− , cpIntro
148− , cpElim
149− , cpId
150− , cpComp
151− , cpFirst
152− , cpSwap
153− , cpSecond
154− , cpPair
155− , cpCopy
156− , cpFanOut
157− , cpLeft
158− , cpMirror
159− , cpRight
160− , cpEither
161− , cpUntag
162− , cpFanIn
163− -- Derived process operators
126+import Control.Distributed.Process.Internal.Closure.TH (remotable, mkStatic)
127+import Control.Distributed.Process.Internal.Closure.Derived
128+ ( -- Static functionals
129+ staticConst
130+ , staticCompose
131+ , staticFirst
132+ , staticSecond
133+ , staticSplit
134+ -- Creating closures
135+ , staticDecode
136+ , staticClosure
137+ , toClosure
138+ -- Closure versions of CH primitives
139+ , cpLink
140+ , cpUnlink
141+ , cpSend
142+ , cpExpect
143+ -- @Closure (Process a)@ as a not-quite-monad
144+ , cpReturn
164145 , cpBind
165146 , cpSeq
166− -- Serialization dictionaries
167− , serializableDictUnit
147+ -- Serialization dictionaries (and their static versions)
148+ , sdictUnit
149+ , sdictUnit__static
150+ , sdictProcessId
151+ , sdictProcessId__static
168152 )
distributed-process/src/Control/Distributed/Process/Internal/Closure/BuiltIn.hs+11−1
@@ -1,5 +1,7 @@
11-- | Built-in closures
2−module Control.Distributed.Process.Internal.Closure.BuiltIn
2+module Control.Distributed.Process.Internal.Closure.BuiltIn () where
3+
4+{-
35 ( -- Combinators
46 closureApply
57 -- TODO
@@ -20,6 +22,7 @@ import Control.Distributed.Process.Internal.Types
2022 , StaticLabel(..)
2123 )
2224import Control.Distributed.Process.Internal.TypeRep () -- Binary instances
25+import Control.Distributed.Process.Serializable (Serializable)
2326
2427--------------------------------------------------------------------------------
2528-- Combinators --
@@ -29,6 +32,12 @@ closureApply :: Closure (a -> b) -> Closure a -> Closure b
2932closureApply (Closure (Static labelf) envf) (Closure (Static labelx) envx) =
3033 Closure (Static ClosureApply) $ encode (labelf, envf, labelx, envx)
3134
35+closureReturn :: Serializable a => Static (SerializableDict a) -> a -> Closure a
36+closureReturn (Static dict) x = Closure (Static ClosureReturn) (encode (dict, x))
37+
38+closureStatic :: Static a -> Closure a
39+closureStatic (Static dict) = Closure (Static ClosureStatic) (encode dict)
40+
3241--------------------------------------------------------------------------------
3342-- Polymorphic closures --
3443-- --
@@ -56,3 +65,4 @@ returnClosure SerializableDict val =
5665expectClosure :: forall a. SerializableDict a -> Closure (Process a)
5766expectClosure SerializableDict =
5867 Closure (Static ClosureExpect) (encode (typeOf (undefined :: a)))
68+-}
distributed-process/src/Control/Distributed/Process/Internal/Closure/Derived.hs+289−155
@@ -1,204 +1,338 @@
11-- | Derived (TH generated) closures
2−module Control.Distributed.Process.Internal.Closure.Derived
3− ( remoteTable
4− -- * Process primitives
5− , linkClosure
6− , unlinkClosure
7− -- * Generic combinators
8− , closureApply
9− , closureConst
10− , closureUnit
11− -- * Arrow combinators for processes
12− , CP
13− , cpIntro
14− , cpElim
15− , cpId
16− , cpComp
17− , cpFirst
18− , cpSwap
19− , cpSecond
20− , cpPair
21− , cpCopy
22− , cpFanOut
23− , cpLeft
24− , cpMirror
25− , cpRight
26− , cpEither
27− , cpUntag
28− , cpFanIn
29− -- * Derived process operators
2+{-# LANGUAGE MagicHash #-}
3+module Control.Distributed.Process.Internal.Closure.Derived
4+ ( -- * Static functionals
5+ staticConst
6+ , staticCompose
7+ , staticFirst
8+ , staticSecond
9+ , staticSplit
10+ -- * Creating closures
11+ , staticDecode
12+ , staticClosure
13+ , toClosure
14+ -- * Closure versions of CH primitives
15+ , cpLink
16+ , cpUnlink
17+ , cpSend
18+ , cpExpect
19+ -- * @Closure (Process a)@ as a not-quite-monad
20+ , cpReturn
3021 , cpBind
3122 , cpSeq
32− -- * Serialization dictionaries
33− , serializableDictUnit
23+ -- * Serialization dictionaries (and their static versions)
24+ , sdictUnit
25+ , sdictUnit__static
26+ , sdictProcessId
27+ , sdictProcessId__static
28+ -- * Runtime support
29+ , __remoteTable
3430 ) where
3531
36−import Prelude hiding (lookup)
37−import Data.Typeable (Typeable)
38−import Data.Tuple (swap)
32+import Data.Binary (encode, decode)
33+import Data.ByteString.Lazy (ByteString, empty)
34+import Data.Typeable (Typeable, typeOf, TypeRep, typeRepTyCon, TyCon)
3935import Control.Applicative ((<$>))
40−import Control.Monad ((>=>))
36+import Control.Monad (join)
37+import Control.Distributed.Process.Serializable (Serializable)
4138import Control.Distributed.Process.Internal.Types
42− ( Closure(..)
39+ ( Closure(Closure)
40+ , SerializableDict(SerializableDict)
41+ , Static(Static)
42+ , StaticLabel
4343 , Process
44− , RemoteTable
45− , SerializableDict(..)
44+ , staticApply
4645 , ProcessId
46+ , LocalNode(remoteTable)
47+ , procMsg
4748 )
48−import Control.Distributed.Process.Internal.TypeRep () -- Binary instances
49−import Control.Distributed.Process.Internal.Closure.TH (remotable, mkClosure)
5049import Control.Distributed.Process.Internal.Primitives
5150 ( link
5251 , unlink
52+ , send
53+ , expect
5354 )
54−import Control.Distributed.Process.Internal.Closure.BuiltIn (closureApply)
55+import Control.Distributed.Process.Internal.Closure.TH (remotable, mkStatic)
56+import Control.Distributed.Process.Internal.MessageT (getLocalNode)
57+import Control.Distributed.Process.Internal.Closure.Resolution (resolveClosure)
58+import Control.Distributed.Process.Internal.Dynamic
59+ ( Dynamic(Dynamic)
60+ , dynBind
61+ , unsafeCoerce#
62+ , dynTypeRep
63+ )
64+import qualified Control.Arrow as Arrow (first, second, (***))
5565
5666--------------------------------------------------------------------------------
57−-- TH stuff --
67+-- Setup: A number of functions that we will pass to 'remotable' --
5868--------------------------------------------------------------------------------
5969
60−idUnit :: () -> ()
61−idUnit = id
70+---- Type specializations of monadic operations on Processes -------------------
6271
6372returnProcess :: a -> Process a
6473returnProcess = return
6574
66−kleisliComposeProcess :: (a -> Process b) -> (b -> Process c) -> a -> Process c
67−kleisliComposeProcess = (>=>)
68−
69−firstProcess :: (a -> Process b) -> (a, c) -> Process (b, c)
70−firstProcess f (a, c) = f a >>= \b -> return (b, c)
71−
72−swapProcess :: (a, b) -> Process (b, a)
73−swapProcess = return . swap
74−
75−copyProcess :: a -> Process (a, a)
76−copyProcess x = return (x, x)
77−
78−leftProcess :: (a -> Process b) -> Either a c -> Process (Either b c)
79−leftProcess f (Left a) = Left <$> f a
80−leftProcess _ (Right b) = Right <$> return b
81−
82−mirrorProcess :: Either a b -> Process (Either b a)
83−mirrorProcess (Left x) = Right <$> return x
84−mirrorProcess (Right y) = Left <$> return y
85−
86−untagProcess :: Either a a -> Process a
87−untagProcess (Left x) = return x
88−untagProcess (Right y) = return y
89−
90−serializableDictUnit :: SerializableDict ()
91−serializableDictUnit = SerializableDict
92−
93−remotable [ 'const
94− , 'idUnit
95− , 'returnProcess
96− , 'kleisliComposeProcess
97− , 'firstProcess
98− , 'swapProcess
99− , 'copyProcess
100− , 'leftProcess
101− , 'mirrorProcess
102− , 'untagProcess
75+bindProcess :: Process a -> (a -> Process b) -> Process b
76+bindProcess = (>>=)
77+
78+joinProcess :: Process (Process a) -> Process a
79+joinProcess = join
80+
81+---- Functionals ---------------------------------------------------------------
82+
83+compose :: (b -> c) -> (a -> b) -> a -> c
84+compose = (.)
85+
86+first :: (a -> b) -> (a, c) -> (b, c)
87+first = Arrow.first
88+
89+second :: (a -> b) -> (c, a) -> (c, b)
90+second = Arrow.second
91+
92+split :: (a -> b) -> (a' -> b') -> (a, a') -> (b, b')
93+split = (Arrow.***)
94+
95+---- Variations on standard or CH functions with an explicit dictionary arg ----
96+
97+decodeDict :: SerializableDict a -> ByteString -> a
98+decodeDict SerializableDict = decode
99+
100+sendDict :: SerializableDict a -> ProcessId -> a -> Process ()
101+sendDict SerializableDict = send
102+
103+expectDict :: SerializableDict a -> Process a
104+expectDict SerializableDict = expect
105+
106+---- Serialization dictionaries ------------------------------------------------
107+
108+-- | Serialization dictionary for '()'
109+--
110+-- Use @$(mkStatic sdictUnit)@ (instead of 'sdictUnit__static')
111+-- to refer to the static dictionary.
112+sdictUnit :: SerializableDict ()
113+sdictUnit = SerializableDict
114+
115+-- | Serialization dictionary for 'ProcessId'
116+--
117+-- Use @$(mkStatic sdictProcessId)@ (instead of 'sdictProcessId__static')
118+-- to refer to the static dictionary.
119+sdictProcessId :: SerializableDict ProcessId
120+sdictProcessId = SerializableDict
121+
122+-- | Specialized serialization dictionary required in 'cpBind'
123+sdictBind :: SerializableDict (((StaticLabel, ByteString), (StaticLabel, ByteString)), TypeRep)
124+sdictBind = SerializableDict
125+
126+---- Some specialised processes necessary to implement the combinators ---------
127+
128+unClosure :: (StaticLabel, ByteString) -> Process Dynamic
129+unClosure (label, env) = do
130+ rtable <- remoteTable <$> procMsg getLocalNode
131+ case resolveClosure rtable label env of
132+ Nothing -> fail "Derived.unClosure: resolveClosure failed"
133+ Just dyn -> return dyn
134+
135+unDynamic :: (Process Dynamic, TypeRep) -> Process a
136+unDynamic (pdyn, typ) = do
137+ Dynamic typ' val <- pdyn
138+ if typ == typ'
139+ then return (unsafeCoerce# val)
140+ else fail $ "unDynamic: cannot match "
141+ ++ show typ'
142+ ++ " against expected type "
143+ ++ show typ
144+
145+bindDyn :: (Process Dynamic, Process Dynamic) -> Process Dynamic
146+bindDyn (px, pf) = do
147+ x <- px
148+ f <- pf
149+ case dynBind tyConProcess bindProcess x f of
150+ Just dyn -> return dyn
151+ Nothing -> fail $ "bindDyn: could not bind "
152+ ++ show (dynTypeRep x)
153+ ++ " to "
154+ ++ show (dynTypeRep f)
155+ where
156+ tyConProcess :: TyCon
157+ tyConProcess = typeRepTyCon (typeOf (undefined :: Process ()))
158+
159+---- Finally, the call to remotable --------------------------------------------
160+
161+remotable [ -- Monadic operations
162+ 'returnProcess
163+ , 'bindProcess
164+ , 'joinProcess
165+ -- Functionals (predefined)
166+ , 'const
167+ -- Functionals (defined above)
168+ , 'compose
169+ , 'first
170+ , 'second
171+ , 'split
172+ -- CH primitives
103173 , 'link
104174 , 'unlink
105− , 'serializableDictUnit
175+ -- Explicit dictionaries
176+ , 'decodeDict
177+ , 'sendDict
178+ , 'expectDict
179+ -- Serialization dictionaries
180+ , 'sdictUnit
181+ , 'sdictProcessId
182+ , 'sdictBind
183+ -- Specialized processes
184+ , 'unClosure
185+ , 'unDynamic
186+ , 'bindDyn
106187 ]
107188
108−remoteTable :: RemoteTable -> RemoteTable
109−remoteTable = __remoteTable
110−
111189--------------------------------------------------------------------------------
112−-- Process primitives --
190+-- Static versions of the functionals --
191+-- (We give these explicit names because they are useful outside this module) --
113192--------------------------------------------------------------------------------
114193
115−-- | Closure version of 'link'
116−linkClosure :: ProcessId -> Closure (Process ())
117−linkClosure = $(mkClosure 'link)
194+-- | Static version of 'const'
195+staticConst :: (Typeable a, Typeable b) => Static (a -> b -> a)
196+staticConst = $(mkStatic 'const)
118197
119−-- | Closure version of 'unlink'
120−unlinkClosure :: ProcessId -> Closure (Process ())
121−unlinkClosure = $(mkClosure 'unlink)
198+-- | Static version of ('Prelude..')
199+staticCompose :: (Typeable a, Typeable b, Typeable c)
200+ => Static (b -> c) -> Static (a -> b) -> Static (a -> c)
201+staticCompose f x = $(mkStatic 'compose) `staticApply` f `staticApply` x
122202
123−--------------------------------------------------------------------------------
124−-- Generic closure combinators --
125−--------------------------------------------------------------------------------
203+-- | Static version of 'Control.Arrow.first'
204+staticFirst :: (Typeable a, Typeable b, Typeable c)
205+ => Static ((a -> b) -> (a, c) -> (b, c))
206+staticFirst = $(mkStatic 'first)
126207
127−closureConst :: (Typeable a, Typeable b) => Closure (a -> b -> a)
128−closureConst = $(mkClosure 'const)
208+-- | Static version of 'Control.Arrow.second'
209+staticSecond :: (Typeable a, Typeable b, Typeable c)
210+ => Static ((a -> b) -> (c, a) -> (c, b))
211+staticSecond = $(mkStatic 'second)
129212
130−closureUnit :: Closure ()
131−closureUnit = $(mkClosure 'idUnit) ()
213+-- | Static version of ('Control.Arrow.***')
214+staticSplit :: (Typeable a, Typeable b, Typeable c, Typeable d)
215+ => Static (a -> c) -> Static (b -> d) -> Static ((a, b) -> (c, d))
216+staticSplit f g = $(mkStatic 'split) `staticApply` f `staticApply` g
132217
133218--------------------------------------------------------------------------------
134−-- Arrow combinators for processes --
219+-- Creating closures --
135220--------------------------------------------------------------------------------
136221
137−type CP a b = Closure (a -> Process b)
138−
139−cpIntro :: (Typeable a, Typeable b)
140− => Closure (Process b) -> CP a b
141−cpIntro = closureApply closureConst
222+staticDecode :: Typeable a => Static (SerializableDict a) -> Static (ByteString -> a)
223+staticDecode dict = $(mkStatic 'decodeDict) `staticApply` dict
142224
143−cpElim :: Typeable a
144− => CP () a -> Closure (Process a)
145−cpElim = flip closureApply closureUnit
225+staticClosure :: forall a. Typeable a => Static a -> Closure a
226+staticClosure static = Closure decoder empty
227+ where
228+ decoder :: Static (ByteString -> a)
229+ decoder = staticConst `staticApply` static
146230
147−cpId :: Typeable a => CP a a
148−cpId = $(mkClosure 'returnProcess)
231+toClosure :: forall a. Serializable a
232+ => Static (SerializableDict a) -> a -> Closure a
233+toClosure dict x = Closure decoder (encode x)
234+ where
235+ decoder :: Static (ByteString -> a)
236+ decoder = $(mkStatic 'decodeDict) `staticApply` dict
149237
150−cpComp :: (Typeable a, Typeable b, Typeable c) => CP a b -> CP b c -> CP a c
151−cpComp f g = $(mkClosure 'kleisliComposeProcess) `closureApply` f `closureApply` g
152−
153−cpFirst :: (Typeable a, Typeable b, Typeable c) => CP a b -> CP (a, c) (b, c)
154−cpFirst = closureApply $(mkClosure 'firstProcess)
155−
156−cpSwap :: (Typeable a, Typeable b) => CP (a, b) (b, a)
157−cpSwap = $(mkClosure 'swapProcess)
158−
159−cpSecond :: (Typeable a, Typeable b, Typeable c) => CP a b -> CP (c, a) (c, b)
160−cpSecond f = cpSwap `cpComp` cpFirst f `cpComp` cpSwap
161−
162−cpPair :: (Typeable a, Typeable a', Typeable b, Typeable b')
163− => CP a b -> CP a' b' -> CP (a, a') (b, b')
164−cpPair f g = cpFirst f `cpComp` cpSecond g
165−
166−cpCopy :: Typeable a => CP a (a, a)
167−cpCopy = $(mkClosure 'copyProcess)
168−
169−cpFanOut :: (Typeable a, Typeable b, Typeable c)
170− => CP a b -> CP a c -> CP a (b, c)
171−cpFanOut f g = cpCopy `cpComp` (f `cpPair` g)
172−
173−cpLeft :: (Typeable a, Typeable b, Typeable c)
174− => CP a b -> CP (Either a c) (Either b c)
175−cpLeft = closureApply $(mkClosure 'leftProcess)
176−
177−cpMirror :: (Typeable a, Typeable b) => CP (Either a b) (Either b a)
178−cpMirror = $(mkClosure 'mirrorProcess)
179−
180−cpRight :: (Typeable a, Typeable b, Typeable c)
181− => CP a b -> CP (Either c a) (Either c b)
182−cpRight f = cpMirror `cpComp` cpLeft f `cpComp` cpMirror
183−
184−cpEither :: (Typeable a, Typeable a', Typeable b, Typeable b')
185− => CP a b -> CP a' b' -> CP (Either a a') (Either b b')
186−cpEither f g = cpLeft f `cpComp` cpRight g
238+--------------------------------------------------------------------------------
239+-- Closure versions of CH primitives --
240+--------------------------------------------------------------------------------
187241
188−cpUntag :: Typeable a => CP (Either a a) a
189−cpUntag = $(mkClosure 'untagProcess)
242+-- | Closure version of 'link'
243+cpLink :: ProcessId -> Closure (Process ())
244+cpLink pid = Closure decoder (encode pid)
245+ where
246+ decoder :: Static (ByteString -> Process ())
247+ decoder = $(mkStatic 'link)
248+ `staticCompose`
249+ ($(mkStatic 'decodeDict) `staticApply` $(mkStatic 'sdictProcessId))
190250
191−cpFanIn :: (Typeable a, Typeable b, Typeable c)
192− => CP a c -> CP b c -> CP (Either a b) c
193−cpFanIn f g = (f `cpEither` g) `cpComp` cpUntag
251+-- | Closure version of 'unlink'
252+cpUnlink :: ProcessId -> Closure (Process ())
253+cpUnlink pid = Closure decoder (encode pid)
254+ where
255+ decoder :: Static (ByteString -> Process ())
256+ decoder = $(mkStatic 'unlink)
257+ `staticCompose`
258+ ($(mkStatic 'decodeDict) `staticApply` $(mkStatic 'sdictProcessId))
259+
260+-- | Closure version of 'send'
261+cpSend :: forall a. Typeable a
262+ => Static (SerializableDict a) -> ProcessId -> Closure (a -> Process ())
263+cpSend dict pid = Closure decoder (encode pid)
264+ where
265+ decoder :: Static (ByteString -> a -> Process ())
266+ decoder = ($(mkStatic 'sendDict) `staticApply` dict)
267+ `staticCompose`
268+ ($(mkStatic 'decodeDict) `staticApply` $(mkStatic 'sdictProcessId))
269+
270+-- | Closure version of 'expect'
271+cpExpect :: Typeable a => Static (SerializableDict a) -> Closure (Process a)
272+cpExpect dict = staticClosure ($(mkStatic 'expectDict) `staticApply` dict)
194273
195274--------------------------------------------------------------------------------
196−-- Some derived operators for processes --
275+-- (Closure . Process) as a not-quite-monad --
197276--------------------------------------------------------------------------------
198277
199−cpBind :: (Typeable a, Typeable b)
278+-- | Not-quite-monadic 'return'
279+cpReturn :: forall a. Serializable a
280+ => Static (SerializableDict a) -> a -> Closure (Process a)
281+cpReturn dict x = Closure decoder (encode x)
282+ where
283+ decoder :: Static (ByteString -> Process a)
284+ decoder = $(mkStatic 'returnProcess)
285+ `staticCompose`
286+ ($(mkStatic 'decodeDict) `staticApply` dict)
287+
288+-- | Not-quite-monadic bind ('>>=')
289+cpBind :: forall a b. Typeable b
200290 => Closure (Process a) -> Closure (a -> Process b) -> Closure (Process b)
201−cpBind x f = cpElim $ cpIntro x `cpComp` f
202−
291+cpBind (Closure (Static xlabel) xenv) (Closure (Static flabel) fenv) =
292+ let env :: (((StaticLabel, ByteString), (StaticLabel, ByteString)), TypeRep)
293+ env = (((xlabel, xenv), (flabel, fenv)), typeOf (undefined :: Process b))
294+ in Closure decoder (encode env)
295+ where
296+ decoder :: Static (ByteString -> Process b)
297+ decoder = aux8
298+ `staticCompose`
299+ aux7
300+ `staticCompose`
301+ aux6
302+ `staticCompose`
303+ aux1
304+
305+ aux1 :: Static (ByteString -> (((StaticLabel, ByteString), (StaticLabel, ByteString)), TypeRep))
306+ aux1 = $(mkStatic 'decodeDict) `staticApply` $(mkStatic 'sdictBind)
307+
308+ aux2 :: Static ((StaticLabel, ByteString) -> Process Dynamic)
309+ aux2 = $(mkStatic 'unClosure)
310+
311+ aux3 :: Static (((StaticLabel, ByteString), (StaticLabel, ByteString)) -> (Process Dynamic, Process Dynamic))
312+ aux3 = aux2 `staticSplit` aux2
313+
314+ aux4 :: Static ((Process Dynamic, Process Dynamic) -> Process Dynamic)
315+ aux4 = $(mkStatic 'bindDyn)
316+
317+ aux5 :: Static (((StaticLabel, ByteString), (StaticLabel, ByteString)) -> Process Dynamic)
318+ aux5 = aux4 `staticCompose` aux3
319+
320+ aux6 :: Static ((((StaticLabel, ByteString), (StaticLabel, ByteString)), TypeRep) -> (Process Dynamic, TypeRep))
321+ aux6 = staticFirst `staticApply` aux5
322+
323+ aux7 :: Static ((Process Dynamic, TypeRep) -> Process (Process b))
324+ aux7 = $(mkStatic 'unDynamic)
325+
326+ aux8 :: Static (Process (Process b) -> Process b)
327+ aux8 = $(mkStatic 'joinProcess)
328+
329+cpIntro :: forall a b. (Typeable a, Typeable b)
330+ => Closure (Process b) -> Closure (a -> Process b)
331+cpIntro (Closure static env) = Closure decoder env
332+ where
333+ decoder :: Static (ByteString -> a -> Process b)
334+ decoder = staticConst `staticCompose` static
335+
336+-- | Monadic sequencing ('>>')
203337cpSeq :: Closure (Process ()) -> Closure (Process ()) -> Closure (Process ())
204338cpSeq p q = p `cpBind` cpIntro q
distributed-process/src/Control/Distributed/Process/Internal/Closure/Resolution.hs+12−35
@@ -3,16 +3,10 @@ module Control.Distributed.Process.Internal.Closure.Resolution (resolveClosure)
33
44import Data.Accessor ((^.))
55import Data.ByteString.Lazy (ByteString)
6−import Data.Binary (decode)
7−import Data.Typeable (TypeRep)
8−import Control.Applicative ((<$>))
96import Control.Distributed.Process.Internal.Types
107 ( RemoteTable
118 , remoteTableLabel
12− , remoteTableDict
139 , StaticLabel(..)
14− , ProcessId
15− , RuntimeSerializableSupport(..)
1610 )
1711import Control.Distributed.Process.Internal.Dynamic
1812 ( Dynamic(Dynamic)
@@ -21,33 +15,16 @@ import Control.Distributed.Process.Internal.Dynamic
2115 )
2216import Control.Distributed.Process.Internal.TypeRep () -- Binary instances
2317
18+resolveStatic :: RemoteTable -> StaticLabel -> Maybe Dynamic
19+resolveStatic rtable (StaticLabel string typ) = do
20+ Dynamic _ val <- rtable ^. remoteTableLabel string
21+ return (Dynamic typ val)
22+resolveStatic rtable (StaticApply static1 static2) = do
23+ f <- resolveStatic rtable static1
24+ x <- resolveStatic rtable static2
25+ f `dynApply` x
26+
2427resolveClosure :: RemoteTable -> StaticLabel -> ByteString -> Maybe Dynamic
25−-- Generic closure combinators
26−resolveClosure rtable ClosureApply env = do
27− f <- resolveClosure rtable labelf envf
28− x <- resolveClosure rtable labelx envx
29− f `dynApply` x
30− where
31− (labelf, envf, labelx, envx) = decode env
32−-- Built-in closures
33−resolveClosure rtable ClosureSend env = do
34− rss <- rtable ^. remoteTableDict typ
35− rssSend rss `dynApply` toDyn pid
36− where
37− (typ, pid) = decode env :: (TypeRep, ProcessId)
38−resolveClosure rtable ClosureReturn env = do
39− rss <- rtable ^. remoteTableDict typ
40− rssReturn rss `dynApply` toDyn arg
41− where
42− (typ, arg) = decode env :: (TypeRep, ByteString)
43−resolveClosure rtable ClosureExpect env =
44− rssExpect <$> rtable ^. remoteTableDict typ
45− where
46− typ = decode env :: TypeRep
47−-- User defined closures
48−resolveClosure rtable (UserStatic label) env = do
49− val <- rtable ^. remoteTableLabel label
50− dynApply val (toDyn env)
51−resolveClosure rtable (PolyStatic label) env = do
52− Dynamic _ val <- rtable ^. remoteTableLabel label
53− return (Dynamic (decode env) val)
28+resolveClosure rtable static env = do
29+ decoder <- resolveStatic rtable static
30+ decoder `dynApply` toDyn env
distributed-process/src/Control/Distributed/Process/Internal/Closure/TH.hs+45−16
@@ -5,12 +5,10 @@
55module Control.Distributed.Process.Internal.Closure.TH
66 ( -- * User-level API
77 remotable
8− , mkClosure
8+ , mkStatic
99 ) where
1010
1111import Prelude hiding (lookup)
12−import Data.ByteString.Lazy (ByteString)
13−import Data.Binary (encode, decode)
1412import Data.Accessor ((^=))
1513import Data.Typeable (typeOf)
1614import Control.Applicative ((<$>))
@@ -25,7 +23,7 @@ import Language.Haskell.TH
2523 -- Algebraic data types
2624 , Dec
2725 , Exp
28− , Type(AppT, ArrowT, ForallT, VarT)
26+ , Type(AppT, ForallT, VarT)
2927 , Info(VarI)
3028 , TyVarBndr(PlainTV, KindedTV)
3129 , Pred(ClassP)
@@ -44,22 +42,14 @@ import Language.Haskell.TH
4442 )
4543import Control.Distributed.Process.Internal.Types
4644 ( RemoteTable
47− , Closure(..)
4845 , Static(..)
4946 , StaticLabel(..)
50− , Process
51− , ProcessId
5247 , remoteTableLabel
53− , remoteTableDict
54− , SerializableDict(..)
55− , RuntimeSerializableSupport(..)
5648 )
5749import Control.Distributed.Process.Internal.Dynamic
5850 ( Dynamic(..)
59− , toDyn
6051 , unsafeCoerce#
6152 )
62−import Control.Distributed.Process.Internal.Primitives (send, expect)
6353
6454--------------------------------------------------------------------------------
6555-- User-level API --
@@ -73,12 +63,18 @@ remotable ns = do
7363 rtable <- createMetaData inserts
7464 return $ concat closures ++ rtable
7565
66+-- | Construct a static value
67+mkStatic :: Name -> Q Exp
68+mkStatic = varE . staticName
69+
70+{-
7671-- | Create a closure
7772--
7873-- See module documentation header for "Control.Distributed.Process.Closure"
7974-- for a detailed explanation and examples.
8075mkClosure :: Name -> Q Exp
8176mkClosure = varE . closureName
77+-}
8278
8379--------------------------------------------------------------------------------
8480-- Internal (Template Haskell) --
@@ -91,6 +87,42 @@ createMetaData is =
9187 __remoteTable = $(compose is)
9288 |]
9389
90+generateDefs :: Name -> Q ([Dec], Q Exp)
91+generateDefs n = do
92+ mType <- getType n
93+ case mType of
94+ Just (origName, typ) -> do
95+ let (typVars, typ') = case typ of ForallT vars [] mono -> (vars, mono)
96+ _ -> ([], typ)
97+ (static, label) <- generateStatic origName typVars typ'
98+ return (static, [| registerStatic $(stringE label) (Dynamic (error "Polymorphic value") (unsafeCoerce# $(varE origName))) |])
99+ _ ->
100+ fail $ "remotable: " ++ show n ++ " not found"
101+
102+registerStatic :: String -> Dynamic -> RemoteTable -> RemoteTable
103+registerStatic label dyn = remoteTableLabel label ^= Just dyn
104+
105+-- | Generate a static value
106+generateStatic :: Name -> [TyVarBndr] -> Type -> Q ([Dec], String)
107+generateStatic n xs typ = do
108+ staticTyp <- [t| Static |]
109+ closure <- sequence
110+ [ sigD (staticName n) (return (ForallT xs (map typeable xs) (staticTyp `AppT` typ)) )
111+ , sfnD (staticName n) [| Static (StaticLabel $(stringE label) (typeOf (undefined :: $(return typ)))) |]
112+ ]
113+ return (closure, label)
114+ where
115+ label :: String
116+ label = show n
117+
118+ typeable :: TyVarBndr -> Pred
119+ typeable (PlainTV v) = ClassP (mkName "Typeable") [VarT v]
120+ typeable (KindedTV v _) = ClassP (mkName "Typeable") [VarT v]
121+
122+staticName :: Name -> Name
123+staticName n = mkName $ nameBase n ++ "__static"
124+
125+{-
94126-- | Generate the necessary definitions for one function
95127--
96128-- Given an (f :: a -> b) in module M, create:
@@ -154,9 +186,6 @@ generateDecoder n res = [| $(varE n) . decode :: ByteString -> $res |]
154186closureName :: Name -> Name
155187closureName n = mkName $ nameBase n ++ "__closure"
156188
157−registerLabel :: String -> Dynamic -> RemoteTable -> RemoteTable
158−registerLabel label dyn = remoteTableLabel label ^= Just dyn
159−
160189registerSerializableDict :: forall a. SerializableDict a -> RemoteTable -> RemoteTable
161190registerSerializableDict SerializableDict =
162191 let rss = RuntimeSerializableSupport {
@@ -165,6 +194,7 @@ registerSerializableDict SerializableDict =
165194 , rssExpect = toDyn (expect :: Process a)
166195 }
167196 in remoteTableDict (typeOf (undefined :: a)) ^= Just rss
197+-}
168198
169199--------------------------------------------------------------------------------
170200-- Generic Template Haskell auxiliary functions --
@@ -191,4 +221,3 @@ getType name = do
191221-- | Variation on 'funD' which takes a single expression to define the function
192222sfnD :: Name -> Q Exp -> Q Dec
193223sfnD n e = funD n [clause [] (normalB e) []]
194−
distributed-process/src/Control/Distributed/Process/Internal/Dynamic.hs+25−1
@@ -13,14 +13,24 @@ module Control.Distributed.Process.Internal.Dynamic
1313 , dynTypeRep
1414 , dynApply
1515 , dynApp
16+ , dynBind
17+ , dynBind'
1618 , GHC.unsafeCoerce#
1719 ) where
1820
19−import Data.Typeable (Typeable, TypeRep, typeOf, funResultTy)
21+import Data.Typeable
22+ ( Typeable
23+ , TypeRep
24+ , typeOf
25+ , funResultTy
26+ , TyCon
27+ , splitTyConApp
28+ )
2029import qualified GHC.Prim as GHC (Any, unsafeCoerce#)
2130import Data.Maybe (fromMaybe)
2231
2332data Dynamic = Dynamic TypeRep GHC.Any
33+ deriving Typeable
2434
2535toDyn :: Typeable a => a -> Dynamic
2636toDyn x = Dynamic (typeOf x) (GHC.unsafeCoerce# x)
@@ -55,3 +65,17 @@ dynApp f x = fromMaybe (error typeError) (dynApply f x)
5565 typeError = "Type error in dynamic application.\n"
5666 ++ "Can't apply function " ++ show f
5767 ++ " to argument " ++ show x
68+
69+dynBind :: TyCon -> (forall a b. m a -> (a -> m b) -> m b) -> Dynamic -> Dynamic -> Maybe Dynamic
70+dynBind m bind (Dynamic t1 x) (Dynamic t2 f) =
71+ case splitTyConApp t1 of
72+ (m', [a]) | m' == m ->
73+ case funResultTy t2 a of
74+ Just mb -> Just (Dynamic mb (GHC.unsafeCoerce# bind x f))
75+ _ -> Nothing
76+ _ -> Nothing
77+
78+dynBind' :: TyCon -> (forall a b. m a -> (a -> m b) -> m b) -> Dynamic -> Dynamic -> Dynamic
79+dynBind' m bind x f = fromMaybe (error typeError) (dynBind m bind x f)
80+ where
81+ typeError = "Type error in dynamic bind.\nCan't bind " ++ show x ++ " to " ++ show f
distributed-process/src/Control/Distributed/Process/Internal/Types.hs+17−50
@@ -27,10 +27,10 @@ module Control.Distributed.Process.Internal.Types
2727 -- * Closures
2828 , StaticLabel(..)
2929 , Static(..)
30+ , staticApply
3031 , Closure(..)
3132 , RemoteTable(..)
3233 , SerializableDict(..)
33− , RuntimeSerializableSupport(..)
3434 -- * Messages
3535 , Message(..)
3636 , createMessage
@@ -69,9 +69,7 @@ module Control.Distributed.Process.Internal.Types
6969 , typedChannels
7070 , typedChannelWithId
7171 , remoteTableLabels
72− , remoteTableDicts
7372 , remoteTableLabel
74− , remoteTableDict
7573 ) where
7674
7775import Data.Map (Map)
@@ -110,6 +108,7 @@ import Control.Distributed.Process.Serializable
110108 )
111109import Control.Distributed.Process.Internal.CQueue (CQueue)
112110import Control.Distributed.Process.Internal.Dynamic (Dynamic)
111+import Control.Distributed.Process.Internal.TypeRep () -- Binary instances
113112
114113--------------------------------------------------------------------------------
115114-- Node and process identifiers --
@@ -259,33 +258,26 @@ data ReceivePort a =
259258-- Closures --
260259--------------------------------------------------------------------------------
261260
262−data StaticLabel =
263− UserStatic String
264− | PolyStatic String
265− -- Closure combinators
266− | ClosureApply
267− -- Built-in closures
268− | ClosureReturn
269− | ClosureSend
270− | ClosureExpect
261+data StaticLabel =
262+ StaticLabel String TypeRep
263+ | StaticApply StaticLabel StaticLabel
271264 deriving (Typeable, Show)
272265
273−-- | A static value is one that is bound at top-level.
266+-- | A static value is top-level bound or the application of two static values
274267newtype Static a = Static StaticLabel
275268 deriving (Typeable, Show)
276269
270+-- | Apply two static values
271+staticApply :: Static (a -> b) -> Static a -> Static b
272+staticApply (Static f) (Static x) = Static (StaticApply f x)
273+
277274-- | A closure is a static value and an encoded environment
278275data Closure a = Closure (Static (BSL.ByteString -> a)) BSL.ByteString
279276 deriving (Typeable, Show)
280277
281−-- | Used to fake 'static' (see paper)
278+-- | Runtime dictionary for 'unstatic' lookups
282279data RemoteTable = RemoteTable {
283− -- | If the user creates a closure of type @a -> Closure b@ from a function
284− -- @f : a -> b@, then '_remoteTableLabels' should have an entry for "f"
285− -- of type @ByteString -> b@ (basically, @f . encode@)
286− _remoteTableLabels :: Map String Dynamic
287− -- | Runtime counterpart to SerializableDict
288− , _remoteTableDicts :: Map TypeRep RuntimeSerializableSupport
280+ _remoteTableLabels :: Map String Dynamic
289281 }
290282
291283-- | Reification of 'Serializable' (see "Control.Distributed.Process.Closure")
@@ -293,17 +285,6 @@ data SerializableDict a where
293285 SerializableDict :: Serializable a => SerializableDict a
294286 deriving (Typeable)
295287
296−-- | Runtime support for implementing "polymorphic" functions with a
297−-- Serializable qualifier (sendClosure, returnClosure, ..).
298−--
299−-- We don't attempt to keep this minimal, but instead just add functions as
300−-- convenient. This will be replaced anyway once 'static' has been implemented.
301−data RuntimeSerializableSupport = RuntimeSerializableSupport {
302− rssSend :: Dynamic
303− , rssReturn :: Dynamic
304− , rssExpect :: Dynamic
305− }
306−
307288--------------------------------------------------------------------------------
308289-- Messages --
309290--------------------------------------------------------------------------------
@@ -544,22 +525,14 @@ instance Binary Identifier where
544525 _ -> fail "Identifier.get: invalid"
545526
546527instance Binary StaticLabel where
547− put (UserStatic string) = putWord8 0 >> put string
548− put (PolyStatic string) = putWord8 1 >> put string
549− put ClosureApply = putWord8 2
550− put ClosureReturn = putWord8 3
551− put ClosureSend = putWord8 4
552− put ClosureExpect = putWord8 5
528+ put (StaticLabel string typ) = putWord8 0 >> put string >> put typ
529+ put (StaticApply label1 label2) = putWord8 1 >> put label1 >> put label2
553530 get = do
554531 header <- getWord8
555532 case header of
556− 0 -> UserStatic <$> get
557− 1 -> PolyStatic <$> get
558− 2 -> return ClosureApply
559− 3 -> return ClosureReturn
560− 4 -> return ClosureSend
561− 5 -> return ClosureExpect
562− _ -> fail "StaticLabel.get: invalid"
533+ 0 -> StaticLabel <$> get <*> get
534+ 1 -> StaticApply <$> get <*> get
535+ _ -> fail "StaticLabel.get: invalid"
563536
564537instance Binary WhereIsReply where
565538 put (WhereIsReply label mPid) = put label >> put mPid
@@ -599,15 +572,9 @@ typedChannelWithId cid = typedChannels >>> DAC.mapMaybe cid
599572remoteTableLabels :: Accessor RemoteTable (Map String Dynamic)
600573remoteTableLabels = accessor _remoteTableLabels (\ls tbl -> tbl { _remoteTableLabels = ls })
601574
602−remoteTableDicts :: Accessor RemoteTable (Map TypeRep RuntimeSerializableSupport)
603−remoteTableDicts = accessor _remoteTableDicts (\ds tbl -> tbl { _remoteTableDicts = ds })
604−
605575remoteTableLabel :: String -> Accessor RemoteTable (Maybe Dynamic)
606576remoteTableLabel label = remoteTableLabels >>> DAC.mapMaybe label
607577
608−remoteTableDict :: TypeRep -> Accessor RemoteTable (Maybe RuntimeSerializableSupport)
609−remoteTableDict label = remoteTableDicts >>> DAC.mapMaybe label
610−
611578--------------------------------------------------------------------------------
612579-- MessageT monad --
613580--------------------------------------------------------------------------------
distributed-process/src/Control/Distributed/Process/Node.hs+2−2
@@ -121,14 +121,14 @@ import Control.Distributed.Process.Internal.Dynamic (fromDynamic)
121121import Control.Distributed.Process.Internal.Closure.Resolution (resolveClosure)
122122import Control.Distributed.Process.Internal.Node (runLocalProcess)
123123import Control.Distributed.Process.Internal.Primitives (expect, register)
124−import qualified Control.Distributed.Process.Internal.Closure.Derived as Derived (remoteTable)
124+import qualified Control.Distributed.Process.Internal.Closure.Derived as Derived (__remoteTable)
125125
126126--------------------------------------------------------------------------------
127127-- Initialization --
128128--------------------------------------------------------------------------------
129129
130130initRemoteTable :: RemoteTable
131−initRemoteTable = Derived.remoteTable $ RemoteTable Map.empty Map.empty
131+initRemoteTable = Derived.__remoteTable $ RemoteTable Map.empty
132132
133133-- | Initialize a new local node.
134134--
distributed-process/tests/TestClosure.hs+83−58
@@ -1,7 +1,8 @@
11module Main where
22
3−import qualified Data.ByteString.Lazy as BSL (empty)
4−import Data.Typeable (Typeable)
3+import Data.ByteString.Lazy (ByteString, empty)
4+import Data.Typeable (Typeable, typeOf)
5+import Data.Binary (encode)
56import Control.Monad (join, replicateM)
67import Control.Exception (IOException, throw)
78import Control.Concurrent (forkIO, threadDelay)
@@ -15,8 +16,12 @@ import Control.Distributed.Process.Node
1516import Control.Distributed.Process.Internal.Types (Closure(..), Static(..), StaticLabel(..))
1617import TestAuxiliary
1718
18−serializableDictInt :: SerializableDict Int
19−serializableDictInt = SerializableDict
19+sdictInt :: SerializableDict Int
20+sdictInt = SerializableDict
21+
22+factorial :: Int -> Process Int
23+factorial 0 = return 1
24+factorial n = (n *) <$> factorial (n - 1)
2025
2126addInt :: Int -> Int -> Int
2227addInt x y = x + y
@@ -24,42 +29,76 @@ addInt x y = x + y
2429putInt :: Int -> MVar Int -> IO ()
2530putInt = flip putMVar
2631
27−sendInt :: ProcessId -> Int -> Process ()
28−sendInt = send
29−
3032sendPid :: ProcessId -> Process ()
3133sendPid toPid = do
3234 fromPid <- getSelfPid
3335 send toPid fromPid
3436
35−factorial :: Int -> Process Int
36−factorial 0 = return 1
37−factorial n = (n *) <$> factorial (n - 1)
37+wait :: Int -> Process ()
38+wait = liftIO . threadDelay
3839
39−factorialOf :: () -> Int -> Process Int
40−factorialOf () = factorial
40+remotable [ 'factorial
41+ , 'addInt
42+ , 'putInt
43+ , 'sendPid
44+ , 'sdictInt
45+ , 'wait
46+ ]
4147
42−returnForTestApply :: (Closure (Int -> Process Int), Int) -> (Closure (Int -> Process Int), Int)
43−returnForTestApply = id
48+factorialClosure :: Int -> Closure (Process Int)
49+factorialClosure n = Closure decoder (encode n)
50+ where
51+ decoder :: Static (ByteString -> Process Int)
52+ decoder = $(mkStatic 'factorial) `staticCompose` staticDecode $(mkStatic 'sdictInt)
4453
45−wait :: Int -> Process ()
46−wait = liftIO . threadDelay
54+addIntClosure :: Int -> Closure (Int -> Int)
55+addIntClosure n = Closure decoder (encode n)
56+ where
57+ decoder :: Static (ByteString -> Int -> Int)
58+ decoder = $(mkStatic 'addInt) `staticCompose` staticDecode $(mkStatic 'sdictInt)
59+
60+putIntClosure :: Int -> Closure (MVar Int -> IO ())
61+putIntClosure n = Closure decoder (encode n)
62+ where
63+ decoder :: Static (ByteString -> MVar Int -> IO ())
64+ decoder = $(mkStatic 'putInt) `staticCompose` staticDecode $(mkStatic 'sdictInt)
65+
66+sendPidClosure :: ProcessId -> Closure (Process ())
67+sendPidClosure pid = Closure decoder (encode pid)
68+ where
69+ decoder :: Static (ByteString -> Process ())
70+ decoder = $(mkStatic 'sendPid) `staticCompose` staticDecode $(mkStatic 'sdictProcessId)
71+
72+sendFac :: Int -> ProcessId -> Closure (Process ())
73+sendFac n pid = factorialClosure n `cpBind` cpSend $(mkStatic 'sdictInt) pid
74+
75+factorialOf :: Closure (Int -> Process Int)
76+factorialOf = staticClosure $(mkStatic 'factorial)
77+
78+factorial' :: Int -> Closure (Process Int)
79+factorial' n = cpReturn $(mkStatic 'sdictInt) n `cpBind` factorialOf
80+
81+waitClosure :: Int -> Closure (Process ())
82+waitClosure n = Closure decoder (encode n)
83+ where
84+ decoder :: Static (ByteString -> Process ())
85+ decoder = $(mkStatic 'wait) `staticCompose` staticDecode $(mkStatic 'sdictInt)
86+
87+testUnclosure :: Transport -> RemoteTable -> IO ()
88+testUnclosure transport rtable = do
89+ node <- newLocalNode transport rtable
90+ runProcess node $ do
91+ 120 <- join . unClosure $ factorialClosure 5
92+ return ()
4793
48−first :: (a, b) -> a
49−first (x, _) = x
50−
51−$(remotable
52− [ 'addInt
53− , 'putInt
54− , 'sendInt
55− , 'sendPid
56− , 'factorial
57− , 'factorialOf
58− , 'returnForTestApply
59− , 'wait
60− , 'serializableDictInt
61− , 'first
62− ])
94+testBind :: Transport -> RemoteTable -> IO ()
95+testBind transport rtable = do
96+ node <- newLocalNode transport rtable
97+ runProcess node $ do
98+ us <- getSelfPid
99+ join . unClosure $ sendFac 6 us
100+ (720 :: Int) <- expect
101+ return ()
63102
64103testSendPureClosure :: Transport -> RemoteTable -> IO ()
65104testSendPureClosure transport rtable = do
@@ -71,14 +110,14 @@ testSendPureClosure transport rtable = do
71110 addr <- forkProcess node $ do
72111 cl <- expect
73112 fn <- unClosure cl :: Process (Int -> Int)
74− 11 <- return $ fn 6
113+ 13 <- return $ fn 6
75114 liftIO $ putMVar serverDone ()
76115 putMVar serverAddr addr
77116
78117 forkIO $ do
79118 node <- newLocalNode transport rtable
80119 theirAddr <- readMVar serverAddr
81− runProcess node $ send theirAddr ($(mkClosure 'addInt) 5)
120+ runProcess node $ send theirAddr (addIntClosure 7)
82121
83122 takeMVar serverDone
84123
@@ -102,7 +141,7 @@ testSendIOClosure transport rtable = do
102141 forkIO $ do
103142 node <- newLocalNode transport rtable
104143 theirAddr <- readMVar serverAddr
105− runProcess node $ send theirAddr ($(mkClosure 'putInt) 5)
144+ runProcess node $ send theirAddr (putIntClosure 5)
106145
107146 takeMVar serverDone
108147
@@ -124,7 +163,7 @@ testSendProcClosure transport rtable = do
124163 theirAddr <- readMVar serverAddr
125164 runProcess node $ do
126165 pid <- getSelfPid
127− send theirAddr ($(mkClosure 'sendInt) pid)
166+ send theirAddr (cpSend $(mkStatic 'sdictInt) pid)
128167 5 <- expect :: Process Int
129168 liftIO $ putMVar clientDone ()
130169
@@ -144,7 +183,7 @@ testSpawn transport rtable = do
144183 nid <- readMVar serverNodeAddr
145184 runProcess node $ do
146185 pid <- getSelfPid
147− pid' <- spawn nid ($(mkClosure 'sendPid) pid)
186+ pid' <- spawn nid (sendPidClosure pid)
148187 pid'' <- expect
149188 True <- return $ pid' == pid''
150189 liftIO $ putMVar clientDone ()
@@ -164,26 +203,11 @@ testCall transport rtable = do
164203 node <- newLocalNode transport rtable
165204 nid <- readMVar serverNodeAddr
166205 runProcess node $ do
167− (120 :: Int) <- call serializableDictInt nid ($(mkClosure 'factorial) 5)
206+ (120 :: Int) <- call $(mkStatic 'sdictInt) nid (factorialClosure 5)
168207 liftIO $ putMVar clientDone ()
169208
170209 takeMVar clientDone
171210
172−sendFac :: Int -> ProcessId -> Closure (Process ())
173−sendFac n pid = $(mkClosure 'factorial) n `cpBind` $(mkClosure 'sendInt) pid
174−
175−factorial' :: Int -> Closure (Process Int)
176−factorial' n = returnClosure serializableDictInt n `cpBind` $(mkClosure 'factorialOf) ()
177−
178−testBind :: Transport -> RemoteTable -> IO ()
179−testBind transport rtable = do
180− node <- newLocalNode transport rtable
181− runProcess node $ do
182− us <- getSelfPid
183− join . unClosure $ sendFac 6 us
184− (720 :: Int) <- expect
185− return ()
186−
187211testCallBind :: Transport -> RemoteTable -> IO ()
188212testCallBind transport rtable = do
189213 serverNodeAddr <- newEmptyMVar
@@ -197,7 +221,7 @@ testCallBind transport rtable = do
197221 node <- newLocalNode transport rtable
198222 nid <- readMVar serverNodeAddr
199223 runProcess node $ do
200− (120 :: Int) <- call serializableDictInt nid (factorial' 5)
224+ (120 :: Int) <- call $(mkStatic 'sdictInt) nid (factorial' 5)
201225 liftIO $ putMVar clientDone ()
202226
203227 takeMVar clientDone
@@ -227,7 +251,7 @@ testSpawnSupervised transport rtable = do
227251 forkProcess node1 $ do
228252 us <- getSelfPid
229253 liftIO $ putMVar superPid us
230− (child, _ref) <- spawnSupervised (localNodeId node2) ($(mkClosure 'wait) 1000000)
254+ (child, _ref) <- spawnSupervised (localNodeId node2) (waitClosure 1000000)
231255 liftIO $ do
232256 putMVar childPid child
233257 threadDelay 500000 -- Give the child a chance to link to us
@@ -251,7 +275,7 @@ testSpawnInvalid :: Transport -> RemoteTable -> IO ()
251275testSpawnInvalid transport rtable = do
252276 node <- newLocalNode transport rtable
253277 runProcess node $ do
254− (pid, ref) <- spawnMonitor (localNodeId node) (Closure (Static (UserStatic "ThisDoesNotExist")) BSL.empty)
278+ (pid, ref) <- spawnMonitor (localNodeId node) (Closure (Static (StaticLabel "ThisDoesNotExist" (typeOf ()))) empty)
255279 ProcessMonitorNotification ref' pid' _reason <- expect
256280 -- Depending on the exact interleaving, reason might be NoProc or the exception thrown by the absence of the static closure
257281 True <- return $ ref' == ref && pid == pid'
@@ -263,7 +287,7 @@ testClosureExpect transport rtable = do
263287 runProcess node $ do
264288 nodeId <- getSelfNode
265289 us <- getSelfPid
266− them <- spawn nodeId $ expectClosure serializableDictInt `cpBind` $(mkClosure 'sendInt) us
290+ them <- spawn nodeId $ cpExpect $(mkStatic 'sdictInt) `cpBind` cpSend $(mkStatic 'sdictInt) us
267291 send them (1234 :: Int)
268292 (1234 :: Int) <- expect
269293 return ()
@@ -273,12 +297,13 @@ main = do
273297 Right transport <- createTransport "127.0.0.1" "8080" defaultTCPParameters
274298 let rtable = __remoteTable initRemoteTable
275299 runTests
276− [ ("SendPureClosure", testSendPureClosure transport rtable)
300+ [ ("Unclosure", testUnclosure transport rtable)
301+ , ("Bind", testBind transport rtable)
302+ , ("SendPureClosure", testSendPureClosure transport rtable)
277303 , ("SendIOClosure", testSendIOClosure transport rtable)
278304 , ("SendProcClosure", testSendProcClosure transport rtable)
279305 , ("Spawn", testSpawn transport rtable)
280306 , ("Call", testCall transport rtable)
281− , ("Bind", testBind transport rtable)
282307 , ("CallBind", testCallBind transport rtable)
283308 , ("Seq", testSeq transport rtable)
284309 , ("SpawnSupervised", testSpawnSupervised transport rtable)