git.iko.soy/distributed-process
on master help sign in
Merge branch 'master' into dev
commit0551433c33bd197683a856f4756a82799e29ee83 authorEdsko de Vries <edsko@edsko.net> dateFri 10 Aug 2012, 17:17 (14 years ago) parente6701d9e827753
26 files changed+1512−1116
README.md+9−6 distributed-process/distributed-process.cabal+13−12 distributed-process/src/Control/Distributed/Process.hs+27−27 distributed-process/src/Control/Distributed/Process/Closure.hs+39−155 distributed-process/src/Control/Distributed/Process/Internal/Closure/BuiltIn.hs+237 distributed-process/src/Control/Distributed/Process/Internal/Closure/CP.hs−330 distributed-process/src/Control/Distributed/Process/Internal/Closure/MkClosure.hs−25 distributed-process/src/Control/Distributed/Process/Internal/Closure/Resolution.hs−34 distributed-process/src/Control/Distributed/Process/Internal/Closure/Static.hs−190 distributed-process/src/Control/Distributed/Process/Internal/Closure/TH.hs+64−36 distributed-process/src/Control/Distributed/Process/Internal/Dynamic.hs−112 distributed-process/src/Control/Distributed/Process/Internal/Primitives.hs+9−15 distributed-process/src/Control/Distributed/Process/Internal/TypeRep.hs−33 distributed-process/src/Control/Distributed/Process/Internal/Types.hs+6−107 distributed-process/src/Control/Distributed/Process/Node.hs+13−17 distributed-process/src/Control/Distributed/Process/Serializable.hs+6 distributed-process/tests/TestClosure.hs+43−17 distributed-static/LICENSE+30 distributed-static/Setup.hs+2 distributed-static/distributed-static.cabal+37 distributed-static/src/Control/Distributed/Static.hs+435 rank1dynamic/LICENSE+30 rank1dynamic/Setup.hs+2 rank1dynamic/rank1dynamic.cabal+28 rank1dynamic/src/Data/Rank1Dynamic.hs+120 rank1dynamic/src/Data/Rank1Typeable.hs+362
README.md+9−6
@@ -5,12 +5,15 @@ This repository holds an implementation of [Cloud Haskell][1].
55
66At present, this repository hosts
77
8−* network-transport
9−* network-transport-tcp
10−* network-transport-inmemory (incomplete)
11−* distributed-process
12−* distributed-process-simplelocalnet
13−* azure-service-api (skeleton only)
8+* network-transport: Generic Network.Transport API
9+* network-transport-tcp: TCP instantiation of Network.Transport
10+* network-transport-inmemory: In-memory instantiation of Network.Transport (incomplete)
11+* distributed-static: Support for static values
12+* distributed-process: The main Cloud Haskell package
13+* distributed-process-simplelocalnet: Simple backend for local networks
14+* distributed-process-azure: Azure backend for Cloud Haskell (work in progress)
15+* azure-service-api: Haskell bindings for the Azure service API (work in progress)
16+* rank1dynamic: Like Data.Dynamic and Data.Typeable but with support for polymorphic values
1417
1518For more detailed information about the interfaces provided by these packages,
1619please refer to the [distributed-process repository wiki][2]. People who wish
distributed-process/distributed-process.cabal+13−12
@@ -1,5 +1,5 @@
11Name: distributed-process
2−Version: 0.2.3.0
2+Version: 0.3.0
33Cabal-Version: >=1.8
44Build-Type: Simple
55License: BSD3
@@ -43,21 +43,19 @@ Library
4343 time >= 1.2 && < 1.5,
4444 template-haskell >= 2.6 && < 2.8,
4545 random >= 1.0 && < 1.1,
46− ghc-prim >= 0.2 && < 0.3
46+ ghc-prim >= 0.2 && < 0.3,
47+ distributed-static >= 0.1 && < 0.2,
48+ rank1dynamic >= 0.1 && < 0.2,
49+ syb >= 0.3 && < 0.4
4750 Exposed-modules: Control.Distributed.Process,
4851 Control.Distributed.Process.Serializable,
4952 Control.Distributed.Process.Closure,
5053 Control.Distributed.Process.Node,
5154 Control.Distributed.Process.Internal.Primitives,
5255 Control.Distributed.Process.Internal.CQueue,
53− Control.Distributed.Process.Internal.Dynamic,
54− Control.Distributed.Process.Internal.TypeRep,
5556 Control.Distributed.Process.Internal.Types,
56− Control.Distributed.Process.Internal.Closure.Static,
57− Control.Distributed.Process.Internal.Closure.MkClosure,
58− Control.Distributed.Process.Internal.Closure.CP,
5957 Control.Distributed.Process.Internal.Closure.TH,
60− Control.Distributed.Process.Internal.Closure.Resolution,
58+ Control.Distributed.Process.Internal.Closure.BuiltIn,
6159 Control.Distributed.Process.Internal.Node
6260 Extensions: RankNTypes,
6361 ScopedTypeVariables,
@@ -67,7 +65,6 @@ Library
6765 GADTs,
6866 GeneralizedNewtypeDeriving,
6967 DeriveDataTypeable,
70− TemplateHaskell,
7168 CPP
7269 ghc-options: -Wall
7370 HS-Source-Dirs: src
@@ -90,7 +87,9 @@ Test-Suite TestCH
9087 template-haskell >= 2.6 && < 2.8,
9188 random >= 1.0 && < 1.1,
9289 ghc-prim >= 0.2 && < 0.3,
93− ansi-terminal >= 0.5 && < 0.6
90+ ansi-terminal >= 0.5 && < 0.6,
91+ distributed-static >= 0.1 && < 0.2,
92+ rank1dynamic >= 0.1 && < 0.2
9493 Extensions: RankNTypes,
9594 ScopedTypeVariables,
9695 FlexibleInstances,
@@ -99,7 +98,6 @@ Test-Suite TestCH
9998 GADTs,
10099 GeneralizedNewtypeDeriving,
101100 DeriveDataTypeable,
102− TemplateHaskell,
103101 CPP
104102 ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N -fno-warn-unused-do-bind
105103 HS-Source-Dirs: tests src
@@ -122,7 +120,10 @@ Test-Suite TestClosure
122120 template-haskell >= 2.6 && < 2.8,
123121 random >= 1.0 && < 1.1,
124122 ghc-prim >= 0.2 && < 0.3,
125− ansi-terminal >= 0.5 && < 0.6
123+ ansi-terminal >= 0.5 && < 0.6,
124+ distributed-static >= 0.1 && < 0.2,
125+ rank1dynamic >= 0.1 && < 0.2,
126+ syb >= 0.3 && < 0.4
126127 Other-modules: TestAuxiliary
127128 Extensions: RankNTypes,
128129 ScopedTypeVariables,
distributed-process/src/Control/Distributed/Process.hs+27−27
@@ -100,7 +100,6 @@ module Control.Distributed.Process
100100 , spawnChannelLocal
101101 ) where
102102
103−
104103#if ! MIN_VERSION_base(4,6,0)
105104import Prelude hiding (catch)
106105#endif
@@ -109,13 +108,15 @@ import Data.Typeable (Typeable)
109108import Control.Monad.IO.Class (liftIO)
110109import Control.Applicative ((<$>))
111110import Control.Monad.Reader (ask)
111+import Control.Distributed.Static
112+ ( Closure(..)
113+ , Static
114+ , RemoteTable
115+ )
112116import Control.Distributed.Process.Internal.Types
113− ( RemoteTable
114− , NodeId(..)
117+ ( NodeId(..)
115118 , ProcessId(..)
116119 , Process(..)
117− , Closure(..)
118− , Static
119120 , MonitorRef(..)
120121 , ProcessMonitorNotification(..)
121122 , NodeMonitorNotification(..)
@@ -126,29 +127,28 @@ import Control.Distributed.Process.Internal.Types
126127 , DiedReason(..)
127128 , SpawnRef(..)
128129 , DidSpawn(..)
129− , Closure(..)
130130 , SendPort(..)
131131 , ReceivePort(..)
132− , SerializableDict(..)
133132 , SendPortId(..)
134133 , WhereIsReply(..)
135134 , LocalProcess(processNode)
136135 )
137−import Control.Distributed.Process.Internal.Closure.CP
138− ( cpSeq
139− , cpBind
140− , cpSend
141− , cpExpect
136+import Control.Distributed.Process.Serializable (SerializableDict)
137+import Control.Distributed.Process.Internal.Closure.BuiltIn
138+ ( sdictUnit
139+ , sdictSendPort
140+ , sndStatic
141+ , idCP
142+ , seqCP
143+ , bindCP
144+ , splitCP
142145 , cpLink
143146 , cpUnlink
147+ , cpExpect
148+ , cpSend
144149 , cpNewChan
145− , cpCancelL
146− , cpSplit
147− )
148−import Control.Distributed.Process.Internal.Closure.Static
149− ( sdictUnit
150− , sdictSendPort
151150 )
151+import Control.Distributed.Static (closureCompose, staticClosure)
152152import Control.Distributed.Process.Internal.Primitives
153153 ( -- Basic messaging
154154 send
@@ -289,9 +289,9 @@ spawn nid proc = do
289289 -- that was already set up
290290 mRef <- monitorNode nid
291291 sRef <- spawnAsync nid $ cpLink us
292− `cpSeq` cpExpect sdictUnit
293− `cpSeq` cpUnlink us
294− `cpSeq` proc
292+ `seqCP` cpExpect sdictUnit
293+ `seqCP` cpUnlink us
294+ `seqCP` proc
295295 mPid <- receiveWait
296296 [ matchIf (\(DidSpawn ref _) -> ref == sRef)
297297 (\(DidSpawn _ pid) -> return $ Just pid)
@@ -333,7 +333,7 @@ spawnMonitor nid proc = do
333333call :: Serializable a => Static (SerializableDict a) -> NodeId -> Closure (Process a) -> Process a
334334call dict nid proc = do
335335 us <- getSelfPid
336− (_, mRef) <- spawnMonitor nid (proc `cpBind` cpSend dict us)
336+ (_, mRef) <- spawnMonitor nid (proc `bindCP` cpSend dict us)
337337 -- We are guaranteed to receive the reply before the monitor notification
338338 -- (if a reply is sent at all)
339339 -- NOTE: This might not be true if we switch to unreliable delivery.
@@ -353,7 +353,7 @@ spawnSupervised :: NodeId
353353 -> Process (ProcessId, MonitorRef)
354354spawnSupervised nid proc = do
355355 us <- getSelfPid
356− them <- spawn nid (cpLink us `cpSeq` proc)
356+ them <- spawn nid (cpLink us `seqCP` proc)
357357 ref <- monitor them
358358 return (them, ref)
359359
@@ -370,10 +370,10 @@ spawnChannel dict nid proc = do
370370 where
371371 go :: ProcessId -> Closure (Process ())
372372 go pid = cpNewChan dict
373− `cpBind`
374− (cpSend (sdictSendPort dict) pid `cpSplit` proc)
375− `cpBind`
376− cpCancelL
373+ `bindCP`
374+ (cpSend (sdictSendPort dict) pid `splitCP` proc)
375+ `bindCP`
376+ (idCP `closureCompose` staticClosure sndStatic)
377377
378378--------------------------------------------------------------------------------
379379-- Local versions of spawn --
distributed-process/src/Control/Distributed/Process/Closure.hs+39−155
@@ -1,11 +1,14 @@
1−-- | Static values and Closures
1+-- | /Towards Haskell in the Cloud/ (Epstein et al., Haskell Symposium 2011)
2+-- proposes a new type construct called 'static' that characterizes values that
3+-- are known statically. Cloud Haskell uses the
4+-- 'Control.Distributed.Static.Static' implementation from
5+-- "Control.Distributed.Static". That module comes with its own extensive
6+-- documentation, which you should read if you want to know the details. Here
7+-- we explain the Template Haskell support only.
28--
39-- [Static values]
410--
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
11+-- Given a top-level (possibly polymorphic, but unqualified) definition
912--
1013-- > f :: forall a1 .. an. T
1114-- > f = ...
@@ -42,55 +45,13 @@
4245--
4346-- then you need to enable the @ScopedTypeVariables@ language extension.
4447--
45−-- [Dealing with type class qualifiers]
46−--
47−-- Although 'mkStatic' supports polymorphic types, it does not support
48−-- qualified types. For instance, you cannot call 'mkStatic' on
49−--
50−-- > decode :: Serializable a => ByteString -> a
51−--
52−-- Instead, you will need to reify the type class dictionary. Cloud Haskell
53−-- comes with a reified version of 'Serializable':
54−--
55−-- > data SerializableDict a where
56−-- > SerializableDict :: Serializable a => SerializableDict a
57−--
58−-- Using the reified dictionary you can define
59−--
60−-- > decodeDict :: SerializableDict a -> ByteString -> a
61−-- > decodeDict SerializableDict = decode
62−--
63−-- where 'decodeDict' is a normal (unqualified) polymorphic value and hence
64−-- can be passed as an argument to remotable:
65−--
66−-- > $(mkStatic 'decodeDict) :: Typeable a => Static (SerializableDict a -> ByteString -> a)
67−--
68−-- [Composing static values]
69−--
70−-- The version of 'static' provided by this implementation of Cloud Haskell is
71−-- strictly more expressive than the one proposed in the paper, and additionally
72−-- supports
73−--
74−-- > staticApply :: Static (a -> b) -> Static a -> Static b
75−--
76−-- This is extremely useful. For example, Cloud Haskell comes with
77−-- 'staticDecode' defined as
78−--
79−-- > staticDecode :: Typeable a => Static (SerializableDict a) -> Static (ByteString -> a)
80−-- > staticDecode dict = $(mkStatic 'decodeDict) `staticApply` dict
81−--
82−-- 'staticDecode' is used when defining closures (see below), and makes
83−-- essential use of 'staticApply'.
48+-- [Static serialization dictionaries]
8449--
85−-- Support for 'staticApply' also makes it possible to define a rich set of
86−-- combinators on 'static' values, a number of which are provided in this
87−-- module.
50+-- Some Cloud Haskell primitives require static serialization dictionaries (**):
8851--
89−-- [Static serialization dictionaries]
52+-- > call :: Serializable a => Static (SerializableDict a) -> NodeId -> Closure (Process a) -> Process a
9053--
91−-- Many Cloud Haskell primitives (like 'staticDecode', above) require static
92−-- serialization dictionaries. In principle these dictionaries require nothing
93−-- special; for instance, given some serializable type 'T' you can define
54+-- Given some serializable type 'T' you can define
9455--
9556-- > sdictT :: SerializableDict T
9657-- > sdictT = SerializableDict
@@ -135,33 +96,10 @@
13596--
13697-- > $(mkClosure 'f) :: T1 -> Closure T2
13798--
138−-- provided that 'T1' is serializable (*).
139−--
140−-- [Creating closures manually]
141−--
142−-- You don't /need/ to use 'mkClosure', however. Closures are defined exactly
143−-- as described in /Towards Haskell in the Cloud/:
144−--
145−-- > data Closure a = Closure (Static (ByteString -> a)) ByteString
146−--
147−-- The splice @$(mkClosure 'isPrime)@ above expands to (prettified a bit):
148−--
149−-- > let decoder :: Static (ByteString -> Process Bool)
150−-- > decoder = $(mkStatic 'isPrime)
151−-- > `staticCompose`
152−-- > staticDecode $(functionSDict 'isPrime)
153−-- > in Closure decoder (encode n)
154−--
155−-- where 'staticCompose' is composition of static functions. Note that
156−-- 'mkClosure' makes use of the static serialization dictionary
157−-- ('functionSDict') created by 'remotable'.
99+-- provided that 'T1' is serializable (*) (remember to pass 'f' to 'remotable').
158100--
159−-- [Combinators on Closures]
160−--
161−-- Support for 'staticApply' (described above) also means that we can define
162−-- combinators on Closures, and we provide a number of them in this module,
163−-- the most important of which is 'cpBind'. Have a look at the implementation
164−-- of 'Control.Distributed.Process.call' for an example use.
101+-- (You can also create closures manually--see the documentation of
102+-- "Control.Distributed.Static" for examples.)
165103--
166104-- [Example]
167105--
@@ -211,119 +149,65 @@
211149-- a priori if 'T1' is serializable or not due to a bug in the Template
212150-- Haskell libraries (<http://hackage.haskell.org/trac/ghc/ticket/7066>)
213151--
214−-- (**) Even though 'staticDecode' is passed an explicit serialization
215−-- dictionary, we still need the 'Typeable' constraint because
152+-- (**) Even though 'call' is passed an explicit serialization
153+-- dictionary, we still need the 'Serializable' constraint because
216154-- 'Static' is not the /true/ static. If it was, we could 'unstatic'
217155-- the dictionary and pattern match on it to bring the 'Typeable'
218156-- instance into scope, but unless proper 'static' support is added to
219157-- ghc we need both the type class argument and the explicit dictionary.
220158module Control.Distributed.Process.Closure
221− ( -- * Creating static values
159+ ( -- * Template Haskell support for creating static values and closures
222160 remotable
223161 , mkStatic
224− -- * Template-Haskell support for creating closures
225162 , mkClosure
226163 , functionSDict
227164 , functionTDict
228− -- * Primitive operations on static values
229− , staticApply
230− , staticDuplicate
231− -- * Static functionals
232− , staticConst
233− , staticFlip
234− , staticFst
235− , staticSnd
236− , staticCompose
237− , staticFirst
238− , staticSecond
239− , staticSplit
240− -- * Static constants
241− , staticUnit
242− -- * Creating closures
243− , staticDecode
244− , staticClosure
245− , toClosure
246165 -- * Serialization dictionaries (and their static versions)
247166 , SerializableDict(..)
167+ , staticDecode
248168 , sdictUnit
249169 , sdictProcessId
250170 , sdictSendPort
251− -- * Definition of CP and the generalized arrow combinators
171+ -- * The CP type and associated combinators
252172 , CP
253− , cpIntro
254− , cpElim
255− , cpId
256− , cpComp
257− , cpFirst
258− , cpSecond
259− , cpSplit
260− , cpCancelL
261− , cpCancelR
262− -- * Closure versions of CH primitives
173+ , idCP
174+ , splitCP
175+ , returnCP
176+ , bindCP
177+ , seqCP
178+ -- * CP versions of Cloud Haskell primitives
263179 , cpLink
264180 , cpUnlink
265181 , cpSend
266182 , cpExpect
267183 , cpNewChan
268− -- * @Closure (Process a)@ as a not-quite-monad
269− , cpReturn
270− , cpBind
271− , cpSeq
272184 ) where
273185
274−import Control.Distributed.Process.Internal.Types
275− ( SerializableDict(..)
276− , staticApply
277− , staticDuplicate
278− )
186+import Control.Distributed.Process.Serializable (SerializableDict(..))
279187import Control.Distributed.Process.Internal.Closure.TH
280188 ( remotable
281189 , mkStatic
282190 , functionSDict
283191 , functionTDict
192+ , mkClosure
284193 )
285−import Control.Distributed.Process.Internal.Closure.Static
286− ( -- Static functionals
287− staticConst
288− , staticFlip
289− , staticFst
290− , staticSnd
291− , staticCompose
292− , staticFirst
293− , staticSecond
294− , staticSplit
295− -- Static constants
296− , staticUnit
297− -- Creating closures
298− , staticDecode
299− , staticClosure
300− , toClosure
301− -- Serialization dictionaries (and their static versions)
194+import Control.Distributed.Process.Internal.Closure.BuiltIn
195+ ( -- Static dictionaries and associated operations
196+ staticDecode
302197 , sdictUnit
303198 , sdictProcessId
304199 , sdictSendPort
305− )
306−import Control.Distributed.Process.Internal.Closure.MkClosure (mkClosure)
307−import Control.Distributed.Process.Internal.Closure.CP
308− ( -- Definition of CP and the generalized arrow combinators
309− CP
310− , cpIntro
311− , cpElim
312− , cpId
313− , cpComp
314− , cpFirst
315− , cpSecond
316− , cpSplit
317− , cpCancelL
318− , cpCancelR
319− -- Closure versions of CH primitives
200+ -- The CP type and associated combinators
201+ , CP
202+ , idCP
203+ , splitCP
204+ , returnCP
205+ , bindCP
206+ , seqCP
207+ -- CP versions of Cloud Haskell primitives
320208 , cpLink
321209 , cpUnlink
322210 , cpSend
323211 , cpExpect
324212 , cpNewChan
325− -- @Closure (Process a)@ as a not-quite-monad
326− , cpReturn
327− , cpBind
328− , cpSeq
329213 )
distributed-process/src/Control/Distributed/Process/Internal/Closure/BuiltIn.hsnew file+237
@@ -0,0 +1,237 @@
1+module Control.Distributed.Process.Internal.Closure.BuiltIn
2+ ( -- * Remote table
3+ remoteTable
4+ -- * Static dictionaries and associated operations
5+ , staticDecode
6+ , sdictUnit
7+ , sdictProcessId
8+ , sdictSendPort
9+ -- * Some static values
10+ , sndStatic
11+ -- * The CP type and associated combinators
12+ , CP
13+ , idCP
14+ , splitCP
15+ , returnCP
16+ , bindCP
17+ , seqCP
18+ -- * CP versions of Cloud Haskell primitives
19+ , cpLink
20+ , cpUnlink
21+ , cpSend
22+ , cpExpect
23+ , cpNewChan
24+ ) where
25+
26+import Data.ByteString.Lazy (ByteString)
27+import Data.Binary (decode, encode)
28+import Data.Rank1Typeable (Typeable, ANY, ANY1, ANY2, ANY3, ANY4)
29+import Data.Rank1Dynamic (toDynamic)
30+import Control.Distributed.Static
31+ ( RemoteTable
32+ , registerStatic
33+ , Static
34+ , staticLabel
35+ , staticApply
36+ , Closure(Closure)
37+ , closureApplyStatic
38+ , closureApply
39+ , staticCompose
40+ , staticClosure
41+ )
42+import Control.Distributed.Process.Serializable
43+ ( SerializableDict(..)
44+ , Serializable
45+ )
46+import Control.Distributed.Process.Internal.Types
47+ ( Process
48+ , ProcessId
49+ , SendPort
50+ , ReceivePort
51+ )
52+import Control.Distributed.Process.Internal.Primitives
53+ ( link
54+ , unlink
55+ , send
56+ , expect
57+ , newChan
58+ )
59+
60+--------------------------------------------------------------------------------
61+-- Remote table --
62+--------------------------------------------------------------------------------
63+
64+remoteTable :: RemoteTable -> RemoteTable
65+remoteTable =
66+ registerStatic "$decodeDict" (toDynamic (decodeDict :: SerializableDict ANY -> ByteString -> ANY))
67+ . registerStatic "$sdictUnit" (toDynamic (SerializableDict :: SerializableDict ()))
68+ . registerStatic "$sdictProcessId" (toDynamic (SerializableDict :: SerializableDict ProcessId))
69+ . registerStatic "$sdictSendPort_" (toDynamic (sdictSendPort_ :: SerializableDict ANY -> SerializableDict (SendPort ANY)))
70+ . registerStatic "$returnProcess" (toDynamic (return :: ANY -> Process ANY))
71+ . registerStatic "$seqProcess" (toDynamic ((>>) :: Process ANY1 -> Process ANY2 -> Process ANY2))
72+ . registerStatic "$bindProcess" (toDynamic ((>>=) :: Process ANY1 -> (ANY1 -> Process ANY2) -> Process ANY2))
73+ . registerStatic "$decodeProcessId" (toDynamic (decode :: ByteString -> ProcessId))
74+ . registerStatic "$link" (toDynamic link)
75+ . registerStatic "$unlink" (toDynamic unlink)
76+ . registerStatic "$sendDict" (toDynamic (sendDict :: SerializableDict ANY -> ProcessId -> ANY -> Process ()))
77+ . registerStatic "$expectDict" (toDynamic (expectDict :: SerializableDict ANY -> Process ANY))
78+ . registerStatic "$newChanDict" (toDynamic (newChanDict :: SerializableDict ANY -> Process (SendPort ANY, ReceivePort ANY)))
79+ . registerStatic "$cpSplit" (toDynamic (cpSplit :: (ANY1 -> Process ANY3) -> (ANY2 -> Process ANY4) -> (ANY1, ANY2) -> Process (ANY3, ANY4)))
80+ . registerStatic "$snd" (toDynamic (snd :: (ANY1, ANY2) -> ANY2))
81+ where
82+ decodeDict :: forall a. SerializableDict a -> ByteString -> a
83+ decodeDict SerializableDict = decode
84+
85+ sdictSendPort_ :: forall a. SerializableDict a -> SerializableDict (SendPort a)
86+ sdictSendPort_ SerializableDict = SerializableDict
87+
88+ sendDict :: forall a. SerializableDict a -> ProcessId -> a -> Process ()
89+ sendDict SerializableDict = send
90+
91+ expectDict :: forall a. SerializableDict a -> Process a
92+ expectDict SerializableDict = expect
93+
94+ newChanDict :: forall a. SerializableDict a -> Process (SendPort a, ReceivePort a)
95+ newChanDict SerializableDict = newChan
96+
97+ cpSplit :: forall a b c d. (a -> Process c) -> (b -> Process d) -> (a, b) -> Process (c, d)
98+ cpSplit f g (a, b) = do
99+ c <- f a
100+ d <- g b
101+ return (c, d)
102+
103+--------------------------------------------------------------------------------
104+-- Static dictionaries and associated operations --
105+--------------------------------------------------------------------------------
106+
107+-- | Static decoder, given a static serialization dictionary.
108+--
109+-- See module documentation of "Control.Distributed.Process.Closure" for an
110+-- example.
111+staticDecode :: Typeable a => Static (SerializableDict a) -> Static (ByteString -> a)
112+staticDecode dict = decodeDictStatic `staticApply` dict
113+ where
114+ decodeDictStatic :: Typeable a => Static (SerializableDict a -> ByteString -> a)
115+ decodeDictStatic = staticLabel "$decodeDict"
116+
117+-- | Serialization dictionary for '()'
118+sdictUnit :: Static (SerializableDict ())
119+sdictUnit = staticLabel "$sdictUnit"
120+
121+-- | Serialization dictionary for 'ProcessId'
122+sdictProcessId :: Static (SerializableDict ProcessId)
123+sdictProcessId = staticLabel "$sdictProcessId"
124+
125+-- | Serialization dictionary for 'SendPort'
126+sdictSendPort :: Typeable a
127+ => Static (SerializableDict a) -> Static (SerializableDict (SendPort a))
128+sdictSendPort = staticApply (staticLabel "$sdictSendPort_")
129+
130+--------------------------------------------------------------------------------
131+-- Static values --
132+--------------------------------------------------------------------------------
133+
134+sndStatic :: Static ((a, b) -> b)
135+sndStatic = staticLabel "$snd"
136+
137+--------------------------------------------------------------------------------
138+-- The CP type and associated combinators --
139+--------------------------------------------------------------------------------
140+
141+-- | @CP a b@ is a process with input of type @a@ and output of type @b@
142+type CP a b = Closure (a -> Process b)
143+
144+returnProcessStatic :: Typeable a => Static (a -> Process a)
145+returnProcessStatic = staticLabel "$returnProcess"
146+
147+-- | 'CP' version of 'Control.Category.id'
148+idCP :: Typeable a => CP a a
149+idCP = staticClosure returnProcessStatic
150+
151+-- | 'CP' version of ('Control.Arrow.***')
152+splitCP :: (Typeable a, Typeable b, Typeable c, Typeable d)
153+ => CP a c -> CP b d -> CP (a, b) (c, d)
154+splitCP p q = cpSplitStatic `closureApplyStatic` p `closureApply` q
155+ where
156+ cpSplitStatic :: Static ((a -> Process c) -> (b -> Process d) -> (a, b) -> Process (c, d))
157+ cpSplitStatic = staticLabel "$cpSplit"
158+
159+-- | 'CP' version of 'Control.Monad.return'
160+returnCP :: forall a. Serializable a
161+ => Static (SerializableDict a) -> a -> Closure (Process a)
162+returnCP dict x = Closure decoder (encode x)
163+ where
164+ decoder :: Static (ByteString -> Process a)
165+ decoder = returnProcessStatic
166+ `staticCompose`
167+ staticDecode dict
168+
169+-- | 'CP' version of ('Control.Monad.>>')
170+seqCP :: (Typeable a, Typeable b)
171+ => Closure (Process a) -> Closure (Process b) -> Closure (Process b)
172+seqCP p q = seqProcessStatic `closureApplyStatic` p `closureApply` q
173+ where
174+ seqProcessStatic :: (Typeable a, Typeable b)
175+ => Static (Process a -> Process b -> Process b)
176+ seqProcessStatic = staticLabel "$seqProcess"
177+
178+-- | (Not quite the) 'CP' version of ('Control.Monad.>>=')
179+bindCP :: forall a b. (Typeable a, Typeable b)
180+ => Closure (Process a) -> CP a b -> Closure (Process b)
181+bindCP x f = bindProcessStatic `closureApplyStatic` x `closureApply` f
182+ where
183+ bindProcessStatic :: (Typeable a, Typeable b)
184+ => Static (Process a -> (a -> Process b) -> Process b)
185+ bindProcessStatic = staticLabel "$bindProcess"
186+
187+--------------------------------------------------------------------------------
188+-- CP versions of Cloud Haskell primitives --
189+--------------------------------------------------------------------------------
190+
191+decodeProcessIdStatic :: Static (ByteString -> ProcessId)
192+decodeProcessIdStatic = staticLabel "$decodeProcessId"
193+
194+-- | 'CP' version of 'link'
195+cpLink :: ProcessId -> Closure (Process ())
196+cpLink = Closure (linkStatic `staticCompose` decodeProcessIdStatic) . encode
197+ where
198+ linkStatic :: Static (ProcessId -> Process ())
199+ linkStatic = staticLabel "$link"
200+
201+-- | 'CP' version of 'unlink'
202+cpUnlink :: ProcessId -> Closure (Process ())
203+cpUnlink = Closure (unlinkStatic `staticCompose` decodeProcessIdStatic) . encode
204+ where
205+ unlinkStatic :: Static (ProcessId -> Process ())
206+ unlinkStatic = staticLabel "$unlink"
207+
208+-- | 'CP' version of 'send'
209+cpSend :: forall a. Typeable a
210+ => Static (SerializableDict a) -> ProcessId -> CP a ()
211+cpSend dict pid = Closure decoder (encode pid)
212+ where
213+ decoder :: Static (ByteString -> a -> Process ())
214+ decoder = (sendDictStatic `staticApply` dict)
215+ `staticCompose`
216+ decodeProcessIdStatic
217+
218+ sendDictStatic :: Typeable a
219+ => Static (SerializableDict a -> ProcessId -> a -> Process ())
220+ sendDictStatic = staticLabel "$sendDict"
221+
222+-- | 'CP' version of 'expect'
223+cpExpect :: Typeable a => Static (SerializableDict a) -> Closure (Process a)
224+cpExpect dict = staticClosure (expectDictStatic `staticApply` dict)
225+ where
226+ expectDictStatic :: Typeable a => Static (SerializableDict a -> Process a)
227+ expectDictStatic = staticLabel "$expectDict"
228+
229+-- | 'CP' version of 'newChan'
230+cpNewChan :: Typeable a
231+ => Static (SerializableDict a)
232+ -> Closure (Process (SendPort a, ReceivePort a))
233+cpNewChan dict = staticClosure (newChanDictStatic `staticApply` dict)
234+ where
235+ newChanDictStatic :: Typeable a
236+ => Static (SerializableDict a -> Process (SendPort a, ReceivePort a))
237+ newChanDictStatic = staticLabel "$newChanDict"
distributed-process/src/Control/Distributed/Process/Internal/Closure/CP.hsdeleted−330
@@ -1,330 +0,0 @@
1−-- | Combinator for process closures
2−{-# LANGUAGE MagicHash #-}
3−module Control.Distributed.Process.Internal.Closure.CP
4− ( -- * Definition of CP and the generalized arrow combinators
5− CP
6− , cpIntro
7− , cpElim
8− , cpId
9− , cpComp
10− , cpFirst
11− , cpSecond
12− , cpSplit
13− , cpCancelL
14− , cpCancelR
15− -- * Closure versions of CH primitives
16− , cpLink
17− , cpUnlink
18− , cpSend
19− , cpExpect
20− , cpNewChan
21− -- * @Closure (Process a)@ as a not-quite-monad
22− , cpReturn
23− , cpBind
24− , cpSeq
25− -- * Runtime support
26− , __remoteTable
27− ) where
28−
29−import Data.Binary (encode)
30−import Data.ByteString.Lazy (ByteString)
31−import Data.Typeable (Typeable, typeOf, typeRepTyCon, TyCon)
32−import Control.Applicative ((<$>))
33−import Control.Monad ((>=>))
34−import Control.Monad.Reader (ask)
35−import Control.Distributed.Process.Serializable (Serializable)
36−import Control.Distributed.Process.Internal.Types
37− ( Closure(Closure)
38− , SerializableDict(SerializableDict)
39− , Static(Static)
40− , Process
41− , staticApply
42− , staticDuplicate
43− , staticTypeOf
44− , typeOfStaticLabel
45− , ProcessId
46− , LocalNode(remoteTable)
47− , LocalProcess(processNode)
48− , SendPort
49− , ReceivePort
50− )
51−import Control.Distributed.Process.Internal.Primitives
52− ( link
53− , unlink
54− , send
55− , expect
56− , newChan
57− )
58−import Control.Distributed.Process.Internal.Closure.TH (remotable, mkStatic)
59−import Control.Distributed.Process.Internal.Closure.Resolution (resolveClosure)
60−import Control.Distributed.Process.Internal.Closure.Static
61− ( staticCompose
62− , staticDecode
63− , staticClosure
64− , staticSplit
65− , staticConst
66− , staticUnit
67− , staticFlip
68− , staticFst
69− , staticSnd
70− , sdictProcessId
71− )
72−import Control.Distributed.Process.Internal.Closure.MkClosure (mkClosure)
73−import Control.Distributed.Process.Internal.Dynamic
74− ( Dynamic(Dynamic)
75− , unsafeCoerce#
76− , dynTypeRep
77− , dynKleisli
78− )
79−import Control.Distributed.Process.Internal.TypeRep (compareTypeRep)
80−
81−--------------------------------------------------------------------------------
82−-- Setup: A number of functions that we will pass to 'remotable' --
83−--------------------------------------------------------------------------------
84−
85−---- Type specializations of monadic operations on Processes -------------------
86−
87−returnProcess :: a -> Process a
88−returnProcess = return
89−
90−bindProcess :: Process a -> (a -> Process b) -> Process b
91−bindProcess = (>>=)
92−
93−kleisliProcess :: (a -> Process b) -> (b -> Process c) -> a -> Process c
94−kleisliProcess = (>=>)
95−
96−parJoinProcess :: Process (a -> Process b) -> a -> Process b
97−parJoinProcess proc a = proc >>= ($ a)
98−
99−---- Variations on standard or CH functions with an explicit dictionary arg ----
100−
101−sendDict :: SerializableDict a -> ProcessId -> a -> Process ()
102−sendDict SerializableDict = send
103−
104−expectDict :: SerializableDict a -> Process a
105−expectDict SerializableDict = expect
106−
107−newChanDict :: SerializableDict a -> Process (SendPort a, ReceivePort a)
108−newChanDict SerializableDict = newChan
109−
110−---- Serialization dictionaries ------------------------------------------------
111−
112−-- | Specialized serialization dictionary required in 'cpBind'
113−sdictComp :: SerializableDict (ByteString, ByteString)
114−sdictComp = SerializableDict
115−
116−---- Some specialised processes necessary to implement the combinators ---------
117−
118−-- | Resolve a closure
119−unClosure :: Static a -> ByteString -> Process Dynamic
120−unClosure static env = do
121− rtable <- remoteTable . processNode <$> ask
122− case resolveClosure rtable static env of
123− Nothing -> fail "Derived.unClosure: resolveClosure failed"
124− Just dyn -> return dyn
125−
126−-- | Remove a 'Dynamic' constructor, provided that the recorded type matches the
127−-- type of the first static argument (the value of that argument is not used)
128−unDynamic :: Static a -> Process Dynamic -> Process a
129−unDynamic (Static label) pdyn = do
130− Dynamic typ val <- pdyn
131− if typ `compareTypeRep` typeOfStaticLabel label
132− then return (unsafeCoerce# val)
133− else fail $ "unDynamic: cannot match "
134− ++ show typ
135− ++ " against expected type "
136− ++ show (typeOfStaticLabel label)
137−
138−-- | Dynamic kleisli composition
139−--
140−-- The first argument stops remotable from trying to generate a SerializableDict
141−-- for (Process Dynamic, Process Dynamic)
142−kleisliCompDyn :: () -> (Process Dynamic, Process Dynamic) -> Process Dynamic
143−kleisliCompDyn () (pf, pg) = do
144− f <- pf -- a -> Process b
145− g <- pg -- b -> Process c
146− case dynKleisli tyConProcess kleisliProcess f g of
147− Just dyn -> return dyn
148− Nothing -> fail $ "kleisliCompDyn: could not compose "
149− ++ show (dynTypeRep f)
150− ++ " with "
151− ++ show (dynTypeRep g)
152− where
153− tyConProcess :: TyCon
154− tyConProcess = typeRepTyCon (typeOf (undefined :: Process ()))
155−
156−cpFirstAux :: (a -> Process b) -> (a, c) -> Process (b, c)
157−cpFirstAux f (a, c) = f a >>= \b -> return (b, c)
158−
159−cpSecondAux :: (a -> Process b) -> (c, a) -> Process (c, b)
160−cpSecondAux f (c, a) = f a >>= \b -> return (c, b)
161−
162−---- Finally, the call to remotable --------------------------------------------
163−
164−remotable [ -- Monadic operations
165− 'returnProcess
166− , 'bindProcess
167− , 'parJoinProcess
168− -- CH primitives
169− , 'link
170− , 'unlink
171− -- Explicit dictionaries
172− , 'sendDict
173− , 'expectDict
174− , 'newChanDict
175− -- Serialization dictionaries
176− , 'sdictComp
177− -- Specialized processes
178− , 'unClosure
179− , 'unDynamic
180− , 'kleisliCompDyn
181− , 'cpFirstAux
182− , 'cpSecondAux
183− ]
184−
185−--------------------------------------------------------------------------------
186−-- Some derived static functions --
187−--------------------------------------------------------------------------------
188−
189−staticUndynamic :: Typeable a => Static (Process Dynamic -> Process a)
190−staticUndynamic =
191− $(mkStatic 'unDynamic) `staticApply` staticDuplicate (staticTypeOf (undefined :: a))
192−
193−staticKleisliCompDyn :: Static ((Process Dynamic, Process Dynamic) -> Process Dynamic)
194−staticKleisliCompDyn = $(mkStatic 'kleisliCompDyn) `staticApply` staticUnit
195−
196−--------------------------------------------------------------------------------
197−-- Definition of CP and the generalized arrow combinators --
198−--------------------------------------------------------------------------------
199−
200−-- | 'CP a b' represents the closure of a process parameterized by 'a' and
201−-- returning 'b'. 'CP a b' forms a (restricted) generalized arrow
202−-- (<http://www.cs.berkeley.edu/~megacz/garrows/>)
203−type CP a b = Closure (a -> Process b)
204−
205−-- | 'CP' introduction form
206−cpIntro :: forall a b. (Typeable a, Typeable b)
207− => Closure (Process b) -> Closure (a -> Process b)
208−cpIntro (Closure static env) = Closure decoder env
209− where
210− decoder :: Static (ByteString -> a -> Process b)
211− decoder = staticConst `staticCompose` static
212−
213−-- | 'CP' elimination form
214−cpElim :: forall a. Typeable a => CP () a -> Closure (Process a)
215−cpElim (Closure static env) = Closure decoder env
216− where
217− decoder :: Static (ByteString -> Process a)
218− decoder = staticFlip static `staticApply` staticUnit
219−
220−-- | Identity ('Closure' version of 'return')
221−cpId :: Typeable a => CP a a
222−cpId = staticClosure $(mkStatic 'returnProcess)
223−
224−-- | Left-to-right composition ('Closure' version of '>=>')
225−cpComp :: forall a b c. (Typeable a, Typeable b, Typeable c)
226− => CP a b -> CP b c -> CP a c
227−cpComp (Closure fstatic fenv) (Closure gstatic genv) =
228− Closure decoder (encode (fenv, genv))
229− where
230− decoder :: Static (ByteString -> a -> Process c)
231− decoder = $(mkStatic 'parJoinProcess)
232− `staticCompose`
233− staticUndynamic
234− `staticCompose`
235− staticKleisliCompDyn
236− `staticCompose`
237− (staticUnclosure fstatic `staticSplit` staticUnclosure gstatic)
238− `staticCompose`
239− staticDecode $(mkStatic 'sdictComp)
240−
241−-- | First
242−cpFirst :: forall a b c. (Typeable a, Typeable b, Typeable c)
243− => CP a b -> CP (a, c) (b, c)
244−cpFirst (Closure static env) = Closure decoder env
245− where
246− decoder :: Static (ByteString -> (a, c) -> Process (b, c))
247− decoder = $(mkStatic 'cpFirstAux) `staticCompose` static
248−
249−-- | Second
250−cpSecond :: forall a b c. (Typeable a, Typeable b, Typeable c)
251− => CP a b -> CP (c, a) (c, b)
252−cpSecond (Closure static env) = Closure decoder env
253− where
254− decoder :: Static (ByteString -> (c, a) -> Process (c, b))
255− decoder = $(mkStatic 'cpSecondAux) `staticCompose` static
256−
257−-- | Split (Like 'Control.Arrow.***')
258−cpSplit :: (Typeable a, Typeable b, Typeable c, Typeable d)
259− => CP a c -> CP b d -> CP (a, b) (c, d)
260−cpSplit f g = cpFirst f `cpComp` cpSecond g
261−
262−-- | Left cancellation
263−cpCancelL :: Typeable a => CP ((), a) a
264−-- Closure (((), a) -> Process a)
265−cpCancelL = staticClosure ($(mkStatic 'returnProcess) `staticCompose` staticSnd)
266−
267−-- | Right cancellation
268−cpCancelR :: Typeable a => CP (a, ()) a
269−cpCancelR = staticClosure ($(mkStatic 'returnProcess) `staticCompose` staticFst)
270−
271−--------------------------------------------------------------------------------
272−-- Closure versions of CH primitives --
273−--------------------------------------------------------------------------------
274−
275−-- | Closure version of 'link'
276−cpLink :: ProcessId -> Closure (Process ())
277−cpLink = $(mkClosure 'link)
278−
279−-- | Closure version of 'unlink'
280−cpUnlink :: ProcessId -> Closure (Process ())
281−cpUnlink = $(mkClosure 'unlink)
282−
283−-- | Closure version of 'send'
284−cpSend :: forall a. Typeable a
285− => Static (SerializableDict a) -> ProcessId -> Closure (a -> Process ())
286−cpSend dict pid = Closure decoder (encode pid)
287− where
288− decoder :: Static (ByteString -> a -> Process ())
289− decoder = ($(mkStatic 'sendDict) `staticApply` dict)
290− `staticCompose`
291− staticDecode sdictProcessId
292−
293−-- | Closure version of 'expect'
294−cpExpect :: Typeable a => Static (SerializableDict a) -> Closure (Process a)
295−cpExpect dict = staticClosure ($(mkStatic 'expectDict) `staticApply` dict)
296−
297−-- | Closure version of 'newChan'
298−cpNewChan :: Typeable a
299− => Static (SerializableDict a)
300− -> Closure (Process (SendPort a, ReceivePort a))
301−cpNewChan dict = staticClosure ($(mkStatic 'newChanDict) `staticApply` dict)
302−
303−--------------------------------------------------------------------------------
304−-- (Closure . Process) as a not-quite-monad --
305−--------------------------------------------------------------------------------
306−
307−-- | Not-quite-monadic 'return'
308−cpReturn :: forall a. Serializable a
309− => Static (SerializableDict a) -> a -> Closure (Process a)
310−cpReturn dict x = Closure decoder (encode x)
311− where
312− decoder :: Static (ByteString -> Process a)
313− decoder = $(mkStatic 'returnProcess)
314− `staticCompose`
315− staticDecode dict
316−
317−
318−staticUnclosure :: Typeable a
319− => Static a -> Static (ByteString -> Process Dynamic)
320−staticUnclosure s =
321− $(mkStatic 'unClosure) `staticApply` staticDuplicate s
322−
323−-- | Not-quite-monadic bind ('>>=')
324−cpBind :: forall a b. (Typeable a, Typeable b)
325− => Closure (Process a) -> Closure (a -> Process b) -> Closure (Process b)
326−cpBind x f = cpElim (cpIntro x `cpComp` f)
327−
328−-- | Monadic sequencing ('>>')
329−cpSeq :: Closure (Process ()) -> Closure (Process ()) -> Closure (Process ())
330−cpSeq p q = p `cpBind` cpIntro q
distributed-process/src/Control/Distributed/Process/Internal/Closure/MkClosure.hsdeleted−25
@@ -1,25 +0,0 @@
1−{-# LANGUAGE TemplateHaskell #-}
2−module Control.Distributed.Process.Internal.Closure.MkClosure (mkClosure) where
3−
4−import Data.Binary (encode)
5−import Language.Haskell.TH (Q, Exp, Name)
6−import Control.Distributed.Process.Internal.Types (Closure(Closure))
7−import Control.Distributed.Process.Internal.Closure.TH
8− ( mkStatic
9− , functionSDict
10− )
11−import Control.Distributed.Process.Internal.Closure.Static
12− ( staticCompose
13− , staticDecode
14− )
15−
16−-- | Create a closure
17−--
18−-- If @f : T1 -> T2@ is a /monomorphic/ function
19−-- then @$(mkClosure 'f) :: T1 -> Closure T2@.
20−-- Be sure to pass 'f' to 'remotable'.
21−mkClosure :: Name -> Q Exp
22−mkClosure n =
23− [| Closure ($(mkStatic n) `staticCompose` staticDecode $(functionSDict n))
24− . encode
25− |]
distributed-process/src/Control/Distributed/Process/Internal/Closure/Resolution.hsdeleted−34
@@ -1,34 +0,0 @@
1−{-# LANGUAGE MagicHash #-}
2−module Control.Distributed.Process.Internal.Closure.Resolution (resolveClosure) where
3−
4−import Data.Accessor ((^.))
5−import Data.ByteString.Lazy (ByteString)
6−import Control.Distributed.Process.Internal.Types
7− ( RemoteTable
8− , remoteTableLabel
9− , Static(Static)
10− , StaticLabel(..)
11− )
12−import Control.Distributed.Process.Internal.Dynamic
13− ( Dynamic(Dynamic)
14− , toDyn
15− , dynApply
16− , unsafeCoerce#
17− )
18−import Control.Distributed.Process.Internal.TypeRep () -- Binary instances
19−
20−resolveStatic :: RemoteTable -> Static a -> Maybe Dynamic
21−resolveStatic rtable (Static (StaticLabel string typ)) = do
22− Dynamic _ val <- rtable ^. remoteTableLabel string
23− return (Dynamic typ val)
24−resolveStatic rtable (Static (StaticApply static1 static2)) = do
25− f <- resolveStatic rtable (Static static1)
26− x <- resolveStatic rtable (Static static2)
27− f `dynApply` x
28−resolveStatic _rtable (Static (StaticDuplicate static typ)) =
29− return $ Dynamic typ (unsafeCoerce# (Static static))
30−
31−resolveClosure :: RemoteTable -> Static a -> ByteString -> Maybe Dynamic
32−resolveClosure rtable static env = do
33− decoder <- resolveStatic rtable static
34− decoder `dynApply` toDyn env
distributed-process/src/Control/Distributed/Process/Internal/Closure/Static.hsdeleted−190
@@ -1,190 +0,0 @@
1−-- | Combinators on static values
2−{-# LANGUAGE MagicHash #-}
3−module Control.Distributed.Process.Internal.Closure.Static
4− ( -- * Static functionals
5− staticConst
6− , staticFlip
7− , staticFst
8− , staticSnd
9− , staticCompose
10− , staticFirst
11− , staticSecond
12− , staticSplit
13− -- * Static constants
14− , staticUnit
15− -- * Creating closures
16− , staticDecode
17− , staticClosure
18− , toClosure
19− -- * Serialization dictionaries (and their static versions)
20− , sdictUnit
21− , sdictProcessId
22− , sdictSendPort
23− -- * Runtime support
24− , __remoteTable
25− ) where
26−
27−import Data.Binary (encode, decode)
28−import Data.ByteString.Lazy (ByteString, empty)
29−import Data.Typeable (Typeable)
30−import Control.Distributed.Process.Serializable (Serializable)
31−import Control.Distributed.Process.Internal.Types
32− ( Closure(Closure)
33− , SerializableDict(SerializableDict)
34− , Static
35− , staticApply
36− , ProcessId
37− , SendPort
38− )
39−import Control.Distributed.Process.Internal.Closure.TH (remotable, mkStatic)
40−import qualified Control.Arrow as Arrow (first, second, (***))
41−
42−--------------------------------------------------------------------------------
43−-- Setup: A number of functions that we will pass to 'remotable' --
44−--------------------------------------------------------------------------------
45−
46−---- Functionals ---------------------------------------------------------------
47−
48−compose :: (b -> c) -> (a -> b) -> a -> c
49−compose = (.)
50−
51−first :: (a -> b) -> (a, c) -> (b, c)
52−first = Arrow.first
53−
54−second :: (a -> b) -> (c, a) -> (c, b)
55−second = Arrow.second
56−
57−split :: (a -> b) -> (a' -> b') -> (a, a') -> (b, b')
58−split = (Arrow.***)
59−
60−---- Constants -----------------------------------------------------------------
61−
62−unit :: ()
63−unit = ()
64−
65−---- Variations on standard or CH functions with an explicit dictionary arg ----
66−
67−decodeDict :: SerializableDict a -> ByteString -> a
68−decodeDict SerializableDict = decode
69−
70−---- Serialization dictionaries ------------------------------------------------
71−
72−sdictUnit_ :: SerializableDict ()
73−sdictUnit_ = SerializableDict
74−
75−sdictProcessId_ :: SerializableDict ProcessId
76−sdictProcessId_ = SerializableDict
77−
78−sdictSendPort_ :: SerializableDict a -> SerializableDict (SendPort a)
79−sdictSendPort_ SerializableDict = SerializableDict
80−
81−---- Finally, the call to remotable --------------------------------------------
82−
83−remotable [ -- Functionals (predefined)
84− 'const
85− , 'flip
86− , 'fst
87− , 'snd
88− -- Functionals (defined above)
89− , 'compose
90− , 'first
91− , 'second
92− , 'split
93− -- Constants
94− , 'unit
95− -- Explicit dictionaries
96− , 'decodeDict
97− -- Serialization dictionaries
98− , 'sdictUnit_
99− , 'sdictProcessId_
100− , 'sdictSendPort_
101− ]
102−
103−--------------------------------------------------------------------------------
104−-- Static versions of the functionals --
105−-- (We give these explicit names because they are useful outside this module) --
106−--------------------------------------------------------------------------------
107−
108−-- | Static version of 'const'
109−staticConst :: (Typeable a, Typeable b) => Static (a -> b -> a)
110−staticConst = $(mkStatic 'const)
111−
112−-- | Static version of 'flip'
113−staticFlip :: (Typeable a, Typeable b, Typeable c)
114− => Static (a -> b -> c) -> Static (b -> a -> c)
115−staticFlip f = $(mkStatic 'flip) `staticApply` f
116−
117−-- | Static version of 'fst'
118−staticFst :: (Typeable a, Typeable b)
119− => Static ((a, b) -> a)
120−staticFst = $(mkStatic 'fst)
121−
122−-- | Static version of 'snd'
123−staticSnd :: (Typeable a, Typeable b)
124− => Static ((a, b) -> b)
125−staticSnd = $(mkStatic 'snd)
126−
127−-- | Static version of ('Prelude..')
128−staticCompose :: (Typeable a, Typeable b, Typeable c)
129− => Static (b -> c) -> Static (a -> b) -> Static (a -> c)
130−staticCompose f x = $(mkStatic 'compose) `staticApply` f `staticApply` x
131−
132−-- | Static version of 'Control.Arrow.first'
133−staticFirst :: (Typeable a, Typeable b, Typeable c)
134− => Static ((a -> b) -> (a, c) -> (b, c))
135−staticFirst = $(mkStatic 'first)
136−
137−-- | Static version of 'Control.Arrow.second'
138−staticSecond :: (Typeable a, Typeable b, Typeable c)
139− => Static ((a -> b) -> (c, a) -> (c, b))
140−staticSecond = $(mkStatic 'second)
141−
142−-- | Static version of ('Control.Arrow.***')
143−staticSplit :: (Typeable a, Typeable b, Typeable c, Typeable d)
144− => Static (a -> c) -> Static (b -> d) -> Static ((a, b) -> (c, d))
145−staticSplit f g = $(mkStatic 'split) `staticApply` f `staticApply` g
146−
147−--------------------------------------------------------------------------------
148−-- Constants --
149−--------------------------------------------------------------------------------
150−
151−-- | Static version of '()'
152−staticUnit :: Static ()
153−staticUnit = $(mkStatic 'unit)
154−
155−--------------------------------------------------------------------------------
156−-- Dictionaries --
157−--------------------------------------------------------------------------------
158−
159−-- | Serialization dictionary for '()'
160−sdictUnit :: Static (SerializableDict ())
161−sdictUnit = $(mkStatic 'sdictUnit_)
162−
163−-- | Serialization dictionary for 'ProcessId'
164−sdictProcessId :: Static (SerializableDict ProcessId)
165−sdictProcessId = $(mkStatic 'sdictProcessId_)
166−
167−-- | Serialization dictionary for 'SendPort'
168−sdictSendPort :: Typeable a
169− => Static (SerializableDict a) -> Static (SerializableDict (SendPort a))
170−sdictSendPort = staticApply $(mkStatic 'sdictSendPort_)
171−
172−--------------------------------------------------------------------------------
173−-- Creating closures --
174−--------------------------------------------------------------------------------
175−
176−-- | Static decoder, given a static serialization dictionary.
177−--
178−-- See module documentation of "Control.Distributed.Process.Closure" for an
179−-- example.
180−staticDecode :: Typeable a => Static (SerializableDict a) -> Static (ByteString -> a)
181−staticDecode dict = $(mkStatic 'decodeDict) `staticApply` dict
182−
183−-- | Convert a static value into a closure.
184−staticClosure :: forall a. Typeable a => Static a -> Closure a
185−staticClosure static = Closure (staticConst `staticApply` static) empty
186−
187−-- | Convert a serializable value into a closure.
188−toClosure :: forall a. Serializable a
189− => Static (SerializableDict a) -> a -> Closure a
190−toClosure dict x = Closure (staticDecode dict) (encode x)
distributed-process/src/Control/Distributed/Process/Internal/Closure/TH.hs+64−36
@@ -1,18 +1,15 @@
11-- | Template Haskell support
2−--
3−-- (In a separate file for convenience)
4−{-# LANGUAGE MagicHash #-}
2+{-# LANGUAGE TemplateHaskell #-}
53module Control.Distributed.Process.Internal.Closure.TH
64 ( -- * User-level API
75 remotable
86 , mkStatic
97 , functionSDict
108 , functionTDict
9+ , mkClosure
1110 ) where
1211
13−import Prelude hiding (lookup)
14−import Data.Accessor ((^=))
15−import Data.Typeable (typeOf)
12+import Prelude hiding (succ, any)
1613import Control.Applicative ((<$>))
1714import Language.Haskell.TH
1815 ( -- Q monad and operations
@@ -42,19 +39,27 @@ import Language.Haskell.TH
4239 , funD
4340 , sigD
4441 )
45−import Control.Distributed.Process.Internal.Types
42+import Data.Binary (encode)
43+import Data.Generics (everywhereM, mkM, gmapM)
44+import Data.Rank1Dynamic (toDynamic)
45+import Data.Rank1Typeable
46+ ( Zero
47+ , Succ
48+ , TypVar
49+ )
50+import Control.Distributed.Static
4651 ( RemoteTable
47− , Static(Static)
48− , StaticLabel(StaticLabel)
49− , remoteTableLabel
50− , SerializableDict(SerializableDict)
51− , Process
52+ , registerStatic
53+ , Static
54+ , staticLabel
55+ , Closure(Closure)
56+ , staticCompose
5257 )
53−import Control.Distributed.Process.Internal.Dynamic
54− ( Dynamic(..)
55− , unsafeCoerce#
56− , toDyn
58+import Control.Distributed.Process.Internal.Types (Process)
59+import Control.Distributed.Process.Serializable
60+ ( SerializableDict(SerializableDict)
5761 )
62+import Control.Distributed.Process.Internal.Closure.BuiltIn (staticDecode)
5863
5964--------------------------------------------------------------------------------
6065-- User-level API --
@@ -90,6 +95,12 @@ functionSDict = varE . sdictName
9095functionTDict :: Name -> Q Exp
9196functionTDict = varE . tdictName
9297
98+mkClosure :: Name -> Q Exp
99+mkClosure n =
100+ [| Closure ($(mkStatic n) `staticCompose` staticDecode $(functionSDict n))
101+ . encode
102+ |]
103+
93104--------------------------------------------------------------------------------
94105-- Internal (Template Haskell) --
95106--------------------------------------------------------------------------------
@@ -140,10 +151,8 @@ generateDefs n = do
140151 makeStatic origName typVars typ = do
141152 static <- generateStatic origName typVars typ
142153 let dyn = case typVars of
143− [] -> [| toDyn $(varE origName) |]
144− _ -> [| Dynamic (error "Polymorphic value")
145− (unsafeCoerce# $(varE origName))
146− |]
154+ [] -> [| toDynamic $(varE origName) |]
155+ _ -> [| toDynamic ($(varE origName) :: $(monomorphize typVars typ)) |]
147156 return ( static
148157 , [ [| registerStatic $(stringE (show origName)) $dyn |] ]
149158 )
@@ -151,14 +160,37 @@ generateDefs n = do
151160 makeDict :: Name -> Type -> Q ([Dec], [Q Exp])
152161 makeDict dictName typ = do
153162 sdict <- generateDict dictName typ
154− let dyn = [| toDyn (SerializableDict :: SerializableDict $(return typ)) |]
163+ let dyn = [| toDynamic (SerializableDict :: SerializableDict $(return typ)) |]
155164 return ( sdict
156165 , [ [| registerStatic $(stringE (show dictName)) $dyn |] ]
157166 )
158−
159167
160−registerStatic :: String -> Dynamic -> RemoteTable -> RemoteTable
161−registerStatic label dyn = remoteTableLabel label ^= Just dyn
168+-- | Turn a polymorphic type into a monomorphic type using ANY and co
169+monomorphize :: [TyVarBndr] -> Type -> Q Type
170+monomorphize tvs =
171+ let subst = zip (map tyVarBndrName tvs) anys
172+ in everywhereM (mkM (applySubst subst))
173+ where
174+ anys :: [Q Type]
175+ anys = map typVar (iterate succ zero)
176+
177+ typVar :: Q Type -> Q Type
178+ typVar t = [t| TypVar $t |]
179+
180+ zero :: Q Type
181+ zero = [t| Zero |]
182+
183+ succ :: Q Type -> Q Type
184+ succ t = [t| Succ $t |]
185+
186+ applySubst :: [(Name, Q Type)] -> Type -> Q Type
187+ applySubst s (VarT n) =
188+ case lookup n s of
189+ Nothing -> return (VarT n)
190+ Just t -> t
191+ applySubst s t = gmapM (mkM (applySubst s)) t
192+
193+
162194
163195-- | Generate a static value
164196generateStatic :: Name -> [TyVarBndr] -> Type -> Q [Dec]
@@ -170,27 +202,18 @@ generateStatic n xs typ = do
170202 (map typeable xs)
171203 (staticTyp `AppT` typ)
172204 )
173− , sfnD (staticName n)
174− [| Static $ StaticLabel
175− $(stringE (show n))
176− (typeOf (undefined :: $(return typ)))
177− |]
205+ , sfnD (staticName n) [| staticLabel $(stringE (show n)) |]
178206 ]
179207 where
180208 typeable :: TyVarBndr -> Pred
181− typeable (PlainTV v) = ClassP (mkName "Typeable") [VarT v]
182− typeable (KindedTV v _) = ClassP (mkName "Typeable") [VarT v]
209+ typeable tv = ClassP (mkName "Typeable") [VarT (tyVarBndrName tv)]
183210
184211-- | Generate a serialization dictionary with name 'n' for type 'typ'
185212generateDict :: Name -> Type -> Q [Dec]
186213generateDict n typ = do
187214 sequence
188215 [ sigD n $ [t| Static (SerializableDict $(return typ)) |]
189− , sfnD n
190− [| Static $ StaticLabel
191− $(stringE (show n))
192− (typeOf (undefined :: SerializableDict $(return typ)))
193− |]
216+ , sfnD n [| staticLabel $(stringE (show n)) |]
194217 ]
195218
196219staticName :: Name -> Name
@@ -227,3 +250,8 @@ getType name = do
227250-- | Variation on 'funD' which takes a single expression to define the function
228251sfnD :: Name -> Q Exp -> Q Dec
229252sfnD n e = funD n [clause [] (normalB e) []]
253+
254+-- | The name of a type variable binding occurrence
255+tyVarBndrName :: TyVarBndr -> Name
256+tyVarBndrName (PlainTV n) = n
257+tyVarBndrName (KindedTV n _) = n
distributed-process/src/Control/Distributed/Process/Internal/Dynamic.hsdeleted−112
@@ -1,112 +0,0 @@
1−-- | Reimplementation of Dynamic that supports dynBind
2−--
3−-- We don't have access to the internal representation of Dynamic, otherwise
4−-- we would not have to redefine it completely. Note that we use this only
5−-- internally, so the incompatibility with "our" Dynamic from the standard
6−-- Dynamic is not important.
7−{-# LANGUAGE MagicHash #-}
8−module Control.Distributed.Process.Internal.Dynamic
9− ( Dynamic(..)
10− , toDyn
11− , fromDyn
12− , fromDynamic
13− , dynTypeRep
14− , dynApply
15− , dynApp
16− , dynBind
17− , dynBind'
18− , dynKleisli
19− , GHC.unsafeCoerce#
20− ) where
21−
22−import Data.Typeable
23− ( Typeable
24− , TypeRep
25− , typeOf
26− , funResultTy
27− , TyCon
28− , splitTyConApp
29− , mkFunTy
30− )
31−import Data.Typeable.Internal (funTc)
32−import qualified GHC.Prim as GHC (Any, unsafeCoerce#)
33−import Data.Maybe (fromMaybe)
34−
35−data Dynamic = Dynamic TypeRep GHC.Any
36− deriving Typeable
37−
38−toDyn :: Typeable a => a -> Dynamic
39−toDyn x = Dynamic (typeOf x) (GHC.unsafeCoerce# x)
40−
41−fromDyn :: Typeable a => Dynamic -> a -> a
42−fromDyn (Dynamic rep val) a =
43− if rep == typeOf a
44− then GHC.unsafeCoerce# val
45− else a
46−
47−fromDynamic :: forall a. Typeable a => Dynamic -> Maybe a
48−fromDynamic (Dynamic rep val) =
49− if rep == typeOf (undefined :: a)
50− then Just (GHC.unsafeCoerce# val)
51− else Nothing
52−
53−dynTypeRep :: Dynamic -> TypeRep
54−dynTypeRep (Dynamic t _) = t
55−
56−instance Show Dynamic where
57− show = show . dynTypeRep
58−
59−dynApply :: Dynamic -> Dynamic -> Maybe Dynamic
60−dynApply (Dynamic t1 f) (Dynamic t2 x) =
61− case funResultTy t1 t2 of
62− Just t3 -> Just (Dynamic t3 (GHC.unsafeCoerce# f x))
63− Nothing -> Nothing
64−
65−dynApp :: Dynamic -> Dynamic -> Dynamic
66−dynApp f x = fromMaybe (error typeError) (dynApply f x)
67− where
68− typeError = "Type error in dynamic application.\n"
69− ++ "Can't apply function " ++ show f
70− ++ " to argument " ++ show x
71−
72−dynBind :: TyCon -> (forall a b. m a -> (a -> m b) -> m b) -> Dynamic -> Dynamic -> Maybe Dynamic
73−dynBind m bind (Dynamic t1 x) (Dynamic t2 f) =
74− case splitTyConApp t1 of
75− (m', [a]) | m' == m ->
76− case funResultTy t2 a of
77− Just mb -> Just (Dynamic mb (GHC.unsafeCoerce# bind x f))
78− _ -> Nothing
79− _ -> Nothing
80−
81−dynBind' :: TyCon -> (forall a b. m a -> (a -> m b) -> m b) -> Dynamic -> Dynamic -> Dynamic
82−dynBind' m bind x f = fromMaybe (error typeError) (dynBind m bind x f)
83− where
84− typeError = "Type error in dynamic bind.\nCan't bind " ++ show x ++ " to " ++ show f
85−
86−
87−
88−
89−-- | Dynamically typed Kleisli composition
90−dynKleisli :: TyCon
91− -> (forall a b c. (a -> m b) -> (b -> m c) -> a -> m c)
92− -> Dynamic
93− -> Dynamic
94− -> Maybe Dynamic
95−dynKleisli m comp (Dynamic tf f) (Dynamic tg g) =
96− case splitTyConApp tf of
97− (arr1, [a, mb]) | arr1 == funTc ->
98− case splitTyConApp tg of
99− (arr2, [b, mc]) | arr2 == funTc ->
100− case splitTyConApp mb of
101− (m', [b']) | m' == m && b' == b ->
102− case splitTyConApp mc of
103− (m'', [_c]) | m'' == m ->
104− Just (Dynamic (mkFunTy a mc) (GHC.unsafeCoerce# comp f g))
105− _ ->
106− Nothing
107− _ ->
108− Nothing
109− _ ->
110− Nothing
111− _ ->
112− Nothing
distributed-process/src/Control/Distributed/Process/Internal/Primitives.hs+9−15
@@ -66,14 +66,13 @@ import Prelude hiding (catch)
6666#endif
6767
6868import Data.Binary (decode)
69−import Data.Typeable (Typeable, typeOf)
7069import Data.Time.Clock (getCurrentTime)
7170import Data.Time.Format (formatTime)
7271import System.Locale (defaultTimeLocale)
7372import Control.Monad.Reader (ask)
7473import Control.Monad.IO.Class (MonadIO, liftIO)
7574import Control.Applicative ((<$>))
76−import Control.Exception (Exception, throw, throwIO, SomeException)
75+import Control.Exception (Exception, throwIO, SomeException)
7776import qualified Control.Exception as Ex (catch, mask)
7877import Control.Concurrent.MVar (modifyMVar)
7978import Control.Concurrent.Chan (writeChan)
@@ -90,13 +89,15 @@ import Control.Concurrent.STM
9089import Control.Distributed.Process.Internal.CQueue (dequeue, BlockSpec(..))
9190import Control.Distributed.Process.Serializable (Serializable, fingerprint)
9291import Data.Accessor ((^.), (^:), (^=))
92+import Control.Distributed.Static (Closure)
93+import Data.Rank1Typeable (Typeable)
94+import qualified Control.Distributed.Static as Static (unclosure)
9395import Control.Distributed.Process.Internal.Types
9496 ( NodeId(..)
9597 , ProcessId(..)
9698 , LocalNode(..)
9799 , LocalProcess(..)
98100 , Process(..)
99− , Closure(..)
100101 , Message(..)
101102 , MonitorRef(..)
102103 , SpawnRef(..)
@@ -104,7 +105,6 @@ import Control.Distributed.Process.Internal.Types
104105 , ProcessSignal(..)
105106 , monitorCounter
106107 , spawnCounter
107− , Closure(..)
108108 , SendPort(..)
109109 , ReceivePort(..)
110110 , channelCounter
@@ -121,8 +121,6 @@ import Control.Distributed.Process.Internal.Types
121121 , runLocalProcess
122122 )
123123import Control.Distributed.Process.Internal.Node (sendMessage, sendBinary)
124−import Control.Distributed.Process.Internal.Closure.Resolution (resolveClosure)
125−import Control.Distributed.Process.Internal.Dynamic (fromDyn, dynTypeRep)
126124
127125--------------------------------------------------------------------------------
128126-- Basic messaging --
@@ -502,15 +500,11 @@ nsendRemote nid label msg =
502500
503501-- | Deserialize a closure
504502unClosure :: forall a. Typeable a => Closure a -> Process a
505−unClosure (Closure static env) = do
506− rtable <- remoteTable . processNode <$> ask
507− case resolveClosure rtable static env of
508− Nothing -> error $ "Unregistered closure " ++ show static
509− Just dyn -> return $ fromDyn dyn (throw (typeError dyn))
510− where
511− typeError dyn = userError $ "lookupStatic type error: "
512− ++ "cannot match " ++ show (dynTypeRep dyn)
513− ++ " against " ++ show (typeOf (undefined :: a))
503+unClosure closure = do
504+ rtable <- remoteTable . processNode <$> ask
505+ case Static.unclosure rtable closure of
506+ Left err -> fail $ "Could not resolve closure: " ++ err
507+ Right x -> return x
514508
515509--------------------------------------------------------------------------------
516510-- Auxiliary functions --
distributed-process/src/Control/Distributed/Process/Internal/TypeRep.hsdeleted−33
@@ -1,33 +0,0 @@
1−-- | 'Binary' instances for 'TypeRep', and 'TypeRep' equality (bug workaround)
2−{-# OPTIONS_GHC -fno-warn-orphans #-}
3−module Control.Distributed.Process.Internal.TypeRep (compareTypeRep) where
4−
5−import Control.Applicative ((<$>), (<*>))
6−import Data.Binary (Binary(get, put))
7−import Data.Typeable.Internal (TypeRep(..), TyCon(..))
8−import GHC.Fingerprint.Type (Fingerprint(..))
9−
10−instance Binary Fingerprint where
11− put (Fingerprint hi lo) = put hi >> put lo
12− get = Fingerprint <$> get <*> get
13−
14−instance Binary TypeRep where
15− put (TypeRep fp tyCon ts) = put fp >> put tyCon >> put ts
16− get = TypeRep <$> get <*> get <*> get
17−
18−instance Binary TyCon where
19− put (TyCon hash package modul name) = put hash >> put package >> put modul >> put name
20− get = TyCon <$> get <*> get <*> get <*> get
21−
22−-- | Compare two type representations
23−--
24−-- For base >= 4.6 this compares fingerprints, but older versions of base
25−-- have a bug in the fingerprint construction
26−-- (<http://hackage.haskell.org/trac/ghc/ticket/5962>)
27−compareTypeRep :: TypeRep -> TypeRep -> Bool
28−#if ! MIN_VERSION_base(4,6,0)
29−compareTypeRep (TypeRep _ con ts) (TypeRep _ con' ts')
30− = con == con' && all (uncurry compareTypeRep) (zip ts ts')
31−#else
32−compareTypeRep = (==)
33−#endif
distributed-process/src/Control/Distributed/Process/Internal/Types.hs+6−107
@@ -3,7 +3,6 @@
33-- We collect all types used internally in a single module because
44-- many of these data types are mutually recursive and cannot be split across
55-- modules.
6−{-# LANGUAGE MagicHash #-}
76module Control.Distributed.Process.Internal.Types
87 ( -- * Node and process identifiers
98 NodeId(..)
@@ -24,16 +23,6 @@ module Control.Distributed.Process.Internal.Types
2423 , TypedChannel(..)
2524 , SendPort(..)
2625 , ReceivePort(..)
27− -- * Closures
28− , StaticLabel(..)
29− , Static(..)
30− , staticApply
31− , staticDuplicate
32− , staticTypeOf
33− , typeOfStaticLabel
34− , Closure(..)
35− , RemoteTable(..)
36− , SerializableDict(..)
3726 -- * Messages
3827 , Message(..)
3928 , createMessage
@@ -70,15 +59,12 @@ module Control.Distributed.Process.Internal.Types
7059 , channelCounter
7160 , typedChannels
7261 , typedChannelWithId
73− , remoteTableLabels
74− , remoteTableLabel
7562 ) where
7663
7764import Data.Map (Map)
7865import Data.Int (Int32)
79−import Data.Maybe (fromJust)
80−import Data.Typeable (Typeable, TypeRep, typeOf, funResultTy)
81−import Data.Binary (Binary(put, get), putWord8, getWord8, encode, Put, Get)
66+import Data.Typeable (Typeable)
67+import Data.Binary (Binary(put, get), putWord8, getWord8, encode)
8268import qualified Data.ByteString as BSS (ByteString, concat)
8369import qualified Data.ByteString.Lazy as BSL
8470 ( ByteString
@@ -108,8 +94,10 @@ import Control.Distributed.Process.Serializable
10894 , showFingerprint
10995 )
11096import Control.Distributed.Process.Internal.CQueue (CQueue)
111−import Control.Distributed.Process.Internal.Dynamic (Dynamic)
112−import Control.Distributed.Process.Internal.TypeRep (compareTypeRep) -- and Binary instances
97+import Control.Distributed.Static (RemoteTable, Closure)
98+
99+-- import Control.Distributed.Process.Internal.Dynamic (Dynamic)
100+-- import Control.Distributed.Process.Internal.TypeRep (compareTypeRep) -- and Binary instances
113101
114102--------------------------------------------------------------------------------
115103-- Node and process identifiers --
@@ -255,62 +243,6 @@ data ReceivePort a =
255243 | ReceivePortRR (TVar [ReceivePort a])
256244 deriving Typeable
257245
258−--------------------------------------------------------------------------------
259−-- Closures --
260−--------------------------------------------------------------------------------
261−
262−data StaticLabel =
263− StaticLabel String TypeRep
264− | StaticApply StaticLabel StaticLabel
265− | StaticDuplicate StaticLabel TypeRep
266− deriving (Typeable, Show)
267−
268−-- | A static value is top-level bound or the application of two static values.
269−--
270−-- You construct static values using 'Control.Distributed.Process.Closure.mkStatic'
271−-- or 'staticApply'. 'Static' has a serializable instance for all /Typeable/ 'a':
272−--
273−-- > instance Typeable a => Serializable (Static a)
274−--
275−-- The 'Typeable' constraint (not present in the original Cloud Haskell paper)
276−-- makes it possible to do a type check during deserialization.
277−newtype Static a = Static StaticLabel
278− deriving (Typeable, Show)
279−
280−-- | Apply two static values
281−staticApply :: Static (a -> b) -> Static a -> Static b
282−staticApply (Static f) (Static x) = Static (StaticApply f x)
283−
284−-- | Co-monadic 'duplicate' for static values
285−staticDuplicate :: forall a. Typeable a => Static a -> Static (Static a)
286−staticDuplicate (Static x) =
287− Static (StaticDuplicate x (typeOf (undefined :: Static a)))
288−
289−staticTypeOf :: forall a. Typeable a => a -> Static a
290−staticTypeOf _ = Static (StaticLabel "undefined" (typeOf (undefined :: a)))
291−
292−typeOfStaticLabel :: StaticLabel -> TypeRep
293−typeOfStaticLabel (StaticLabel _ typ)
294− = typ
295−typeOfStaticLabel (StaticApply f x)
296− = fromJust $ funResultTy (typeOfStaticLabel f) (typeOfStaticLabel x)
297−typeOfStaticLabel (StaticDuplicate _ typ)
298− = typ
299−
300−-- | A closure is a static value and an encoded environment
301−data Closure a = Closure (Static (BSL.ByteString -> a)) BSL.ByteString
302− deriving (Typeable, Show)
303−
304−-- | Runtime dictionary for 'unstatic' lookups
305−data RemoteTable = RemoteTable {
306− _remoteTableLabels :: Map String Dynamic
307− }
308−
309−-- | Reification of 'Serializable' (see "Control.Distributed.Process.Closure")
310−data SerializableDict a where
311− SerializableDict :: Serializable a => SerializableDict a
312− deriving (Typeable)
313−
314246--------------------------------------------------------------------------------
315247-- Messages --
316248--------------------------------------------------------------------------------
@@ -526,10 +458,6 @@ instance Binary DiedReason where
526458 4 -> return DiedUnknownId
527459 _ -> fail "DiedReason.get: invalid"
528460
529−instance Typeable a => Binary (Closure a) where
530− put (Closure static env) = put static >> put env
531− get = Closure <$> get <*> get
532−
533461instance Binary DidSpawn where
534462 put (DidSpawn ref pid) = put ref >> put pid
535463 get = DidSpawn <$> get <*> get
@@ -550,29 +478,6 @@ instance Binary Identifier where
550478 2 -> SendPortIdentifier <$> get
551479 _ -> fail "Identifier.get: invalid"
552480
553−-- We don't want StaticLabel to be its own Binary instance
554−putStaticLabel :: StaticLabel -> Put
555−putStaticLabel (StaticLabel string typ) = putWord8 0 >> put string >> put typ
556−putStaticLabel (StaticApply label1 label2) = putWord8 1 >> putStaticLabel label1 >> putStaticLabel label2
557−putStaticLabel (StaticDuplicate label typ) = putWord8 2 >> putStaticLabel label >> put typ
558−
559−getStaticLabel :: Get StaticLabel
560−getStaticLabel = do
561− header <- getWord8
562− case header of
563− 0 -> StaticLabel <$> get <*> get
564− 1 -> StaticApply <$> getStaticLabel <*> getStaticLabel
565− 2 -> StaticDuplicate <$> getStaticLabel <*> get
566− _ -> fail "StaticLabel.get: invalid"
567−
568−instance Typeable a => Binary (Static a) where
569− put (Static label) = putStaticLabel label
570− get = do
571− label <- getStaticLabel
572− if typeOfStaticLabel label `compareTypeRep` typeOf (undefined :: a)
573− then return $ Static label
574− else fail "Static.get: type error"
575−
576481instance Binary WhereIsReply where
577482 put (WhereIsReply label mPid) = put label >> put mPid
578483 get = WhereIsReply <$> get <*> get
@@ -613,9 +518,3 @@ typedChannels = accessor _typedChannels (\cs st -> st { _typedChannels = cs })
613518
614519typedChannelWithId :: LocalSendPortId -> Accessor LocalProcessState (Maybe TypedChannel)
615520typedChannelWithId cid = typedChannels >>> DAC.mapMaybe cid
616−
617−remoteTableLabels :: Accessor RemoteTable (Map String Dynamic)
618−remoteTableLabels = accessor _remoteTableLabels (\ls tbl -> tbl { _remoteTableLabels = ls })
619−
620−remoteTableLabel :: String -> Accessor RemoteTable (Maybe Dynamic)
621−remoteTableLabel label = remoteTableLabels >>> DAC.mapMaybe label
distributed-process/src/Control/Distributed/Process/Node.hs+13−17
@@ -9,7 +9,6 @@ module Control.Distributed.Process.Node
99 , localNodeId
1010 ) where
1111
12−
1312#if ! MIN_VERSION_base(4,6,0)
1413import Prelude hiding (catch)
1514#endif
@@ -31,7 +30,7 @@ import qualified Data.List as List (delete, (\\))
3130import Data.Set (Set)
3231import qualified Data.Set as Set (empty, insert, delete, member, (\\), fromList)
3332import Data.Foldable (forM_)
34−import Data.Maybe (isJust, fromMaybe)
33+import Data.Maybe (isJust)
3534import Data.Typeable (Typeable)
3635import Control.Category ((>>>))
3736import Control.Applicative ((<$>))
@@ -69,9 +68,10 @@ import qualified Network.Transport as NT
6968import Data.Accessor (Accessor, accessor, (^.), (^=), (^:))
7069import qualified Data.Accessor.Container as DAC (mapDefault, mapMaybe)
7170import System.Random (randomIO)
71+import Control.Distributed.Static (RemoteTable, Closure)
72+import qualified Control.Distributed.Static as Static (unclosure)
7273import Control.Distributed.Process.Internal.Types
73− ( RemoteTable
74− , NodeId(..)
74+ ( NodeId(..)
7575 , LocalProcessId(..)
7676 , ProcessId(..)
7777 , LocalNode(..)
@@ -98,7 +98,6 @@ import Control.Distributed.Process.Internal.Types
9898 , DidUnlinkPort(..)
9999 , SpawnRef
100100 , DidSpawn(..)
101− , Closure(..)
102101 , Message
103102 , TypedChannel(..)
104103 , Identifier(..)
@@ -107,31 +106,26 @@ import Control.Distributed.Process.Internal.Types
107106 , typedChannelWithId
108107 , WhereIsReply(..)
109108 , messageToPayload
110− , RemoteTable(..)
111109 , payloadToMessage
112110 , createMessage
113111 , runLocalProcess
114112 )
115113import Control.Distributed.Process.Serializable (Serializable)
116−import Control.Distributed.Process.Internal.Dynamic (fromDynamic)
117−import Control.Distributed.Process.Internal.Closure.Resolution (resolveClosure)
118114import Control.Distributed.Process.Internal.Node
119115 ( sendBinary
120116 , sendMessage
121117 , sendPayload
122118 )
123119import Control.Distributed.Process.Internal.Primitives (expect, register, finally)
124−import qualified Control.Distributed.Process.Internal.Closure.Static as Static (__remoteTable)
125−import qualified Control.Distributed.Process.Internal.Closure.CP as CP (__remoteTable)
120+import qualified Control.Distributed.Process.Internal.Closure.BuiltIn as BuiltIn (remoteTable)
121+import qualified Control.Distributed.Static as Static (initRemoteTable)
126122
127123--------------------------------------------------------------------------------
128124-- Initialization --
129125--------------------------------------------------------------------------------
130126
131127initRemoteTable :: RemoteTable
132−initRemoteTable = Static.__remoteTable
133− . CP.__remoteTable
134− $ RemoteTable Map.empty
128+initRemoteTable = BuiltIn.remoteTable Static.initRemoteTable
135129
136130-- | Initialize a new local node.
137131newLocalNode :: NT.Transport -> RemoteTable -> IO LocalNode
@@ -509,7 +503,9 @@ ncEffectSpawn pid cProc ref = do
509503 mProc <- unClosure cProc
510504 -- If the closure does not exist, we spawn a process that throws an exception
511505 -- This allows the remote node to find out what's happening
512− let proc = fromMaybe (fail $ "Error: unknown closure " ++ show cProc) mProc
506+ let proc = case mProc of
507+ Left err -> fail $ "Error: Could not resolve closure: " ++ err
508+ Right p -> p
513509 node <- ask
514510 pid' <- liftIO $ forkProcess node proc
515511 liftIO $ sendMessage node
@@ -603,10 +599,10 @@ isLocal :: LocalNode -> Identifier -> Bool
603599isLocal nid ident = nodeOf ident == localNodeId nid
604600
605601-- | Lookup a local closure
606−unClosure :: Typeable a => Closure a -> NC (Maybe a)
607−unClosure (Closure static env) = do
602+unClosure :: Typeable a => Closure a -> NC (Either String a)
603+unClosure closure = do
608604 rtable <- remoteTable <$> ask
609− return (resolveClosure rtable static env >>= fromDynamic)
605+ return (Static.unclosure rtable closure)
610606
611607-- | Check if an identifier refers to a valid local object
612608isValidLocalIdentifier :: Identifier -> NC Bool
distributed-process/src/Control/Distributed/Process/Serializable.hs+6
@@ -6,6 +6,7 @@ module Control.Distributed.Process.Serializable
66 , sizeOfFingerprint
77 , Fingerprint
88 , showFingerprint
9+ , SerializableDict(SerializableDict)
910 ) where
1011
1112import Data.Binary (Binary)
@@ -23,6 +24,11 @@ import qualified Data.ByteString.Internal as BSI ( unsafeCreate
2324import Foreign.Storable (pokeByteOff, peekByteOff, sizeOf)
2425import Foreign.ForeignPtr (withForeignPtr)
2526
27+-- | Reification of 'Serializable' (see "Control.Distributed.Process.Closure")
28+data SerializableDict a where
29+ SerializableDict :: Serializable a => SerializableDict a
30+ deriving (Typeable)
31+
2632-- | Objects that can be sent across the network
2733class (Binary a, Typeable a) => Serializable a
2834instance (Binary a, Typeable a) => Serializable a
distributed-process/tests/TestClosure.hs+43−17
@@ -1,7 +1,7 @@
11module Main where
22
33import Data.ByteString.Lazy (empty)
4−import Data.Typeable (Typeable, typeOf)
4+import Data.Typeable (Typeable)
55import Control.Monad (join, replicateM, forever)
66import Control.Exception (IOException, throw)
77import Control.Concurrent (forkIO, threadDelay)
@@ -12,9 +12,12 @@ import Network.Transport.TCP (createTransport, defaultTCPParameters)
1212import Control.Distributed.Process
1313import Control.Distributed.Process.Closure
1414import Control.Distributed.Process.Node
15−import Control.Distributed.Process.Internal.Types (Static(Static), StaticLabel(StaticLabel))
15+import Control.Distributed.Static (staticLabel, staticClosure)
1616import TestAuxiliary
1717
18+quintuple :: a -> b -> c -> d -> e -> (a, b, c, d, e)
19+quintuple a b c d e = (a, b, c, d, e)
20+
1821sdictInt :: SerializableDict Int
1922sdictInt = SerializableDict
2023
@@ -41,6 +44,7 @@ isPrime n = return . (n `elem`) . takeWhile (<= n) . sieve $ [2..]
4144 where
4245 sieve :: [Integer] -> [Integer]
4346 sieve (p : xs) = p : sieve [x | x <- xs, x `mod` p > 0]
47+ sieve [] = error "Uh oh -- we've run out of primes"
4448
4549-- | First argument indicates empty closure environment
4650typedPingServer :: () -> ReceivePort (SendPort ()) -> Process ()
@@ -56,8 +60,14 @@ remotable [ 'factorial
5660 , 'wait
5761 , 'typedPingServer
5862 , 'isPrime
63+ , 'quintuple
5964 ]
6065
66+-- Just try creating a static polymorphic value
67+staticQuintuple :: (Typeable a, Typeable b, Typeable c, Typeable d, Typeable e)
68+ => Static (a -> b -> c -> d -> e -> (a, b, c, d, e))
69+staticQuintuple = $(mkStatic 'quintuple)
70+
6171factorialClosure :: Int -> Closure (Process Int)
6272factorialClosure = $(mkClosure 'factorial)
6373
@@ -71,13 +81,13 @@ sendPidClosure :: ProcessId -> Closure (Process ())
7181sendPidClosure = $(mkClosure 'sendPid)
7282
7383sendFac :: Int -> ProcessId -> Closure (Process ())
74−sendFac n pid = factorialClosure n `cpBind` cpSend $(mkStatic 'sdictInt) pid
84+sendFac n pid = factorialClosure n `bindCP` cpSend $(mkStatic 'sdictInt) pid
7585
7686factorialOf :: Closure (Int -> Process Int)
7787factorialOf = staticClosure $(mkStatic 'factorial)
7888
7989factorial' :: Int -> Closure (Process Int)
80−factorial' n = cpReturn $(mkStatic 'sdictInt) n `cpBind` factorialOf
90+factorial' n = returnCP $(mkStatic 'sdictInt) n `bindCP` factorialOf
8191
8292waitClosure :: Int -> Closure (Process ())
8393waitClosure = $(mkClosure 'wait)
@@ -85,18 +95,22 @@ waitClosure = $(mkClosure 'wait)
8595testUnclosure :: Transport -> RemoteTable -> IO ()
8696testUnclosure transport rtable = do
8797 node <- newLocalNode transport rtable
88− runProcess node $ do
98+ done <- newEmptyMVar
99+ forkProcess node $ do
89100 120 <- join . unClosure $ factorialClosure 5
90− return ()
101+ liftIO $ putMVar done ()
102+ takeMVar done
91103
92104testBind :: Transport -> RemoteTable -> IO ()
93105testBind transport rtable = do
94106 node <- newLocalNode transport rtable
107+ done <- newEmptyMVar
95108 runProcess node $ do
96109 us <- getSelfPid
97110 join . unClosure $ sendFac 6 us
98111 (720 :: Int) <- expect
99− return ()
112+ liftIO $ putMVar done ()
113+ takeMVar done
100114
101115testSendPureClosure :: Transport -> RemoteTable -> IO ()
102116testSendPureClosure transport rtable = do
@@ -227,12 +241,14 @@ testCallBind transport rtable = do
227241testSeq :: Transport -> RemoteTable -> IO ()
228242testSeq transport rtable = do
229243 node <- newLocalNode transport rtable
244+ done <- newEmptyMVar
230245 runProcess node $ do
231246 us <- getSelfPid
232− join . unClosure $ sendFac 5 us `cpSeq` sendFac 6 us
247+ join . unClosure $ sendFac 5 us `seqCP` sendFac 6 us
233248 120 :: Int <- expect
234249 720 :: Int <- expect
235− return ()
250+ liftIO $ putMVar done ()
251+ takeMVar done
236252
237253-- Test 'spawnSupervised'
238254--
@@ -272,29 +288,34 @@ testSpawnSupervised transport rtable = do
272288testSpawnInvalid :: Transport -> RemoteTable -> IO ()
273289testSpawnInvalid transport rtable = do
274290 node <- newLocalNode transport rtable
275− runProcess node $ do
276− (pid, ref) <- spawnMonitor (localNodeId node) (Closure (Static (StaticLabel "ThisDoesNotExist" (typeOf ()))) empty)
291+ done <- newEmptyMVar
292+ forkProcess node $ do
293+ (pid, ref) <- spawnMonitor (localNodeId node) (Closure (staticLabel "ThisDoesNotExist") empty)
277294 ProcessMonitorNotification ref' pid' _reason <- expect
278295 -- Depending on the exact interleaving, reason might be NoProc or the exception thrown by the absence of the static closure
279296 True <- return $ ref' == ref && pid == pid'
280− return ()
297+ liftIO $ putMVar done ()
298+ takeMVar done
281299
282300testClosureExpect :: Transport -> RemoteTable -> IO ()
283301testClosureExpect transport rtable = do
284302 node <- newLocalNode transport rtable
303+ done <- newEmptyMVar
285304 runProcess node $ do
286305 nodeId <- getSelfNode
287306 us <- getSelfPid
288− them <- spawn nodeId $ cpExpect $(mkStatic 'sdictInt) `cpBind` cpSend $(mkStatic 'sdictInt) us
307+ them <- spawn nodeId $ cpExpect $(mkStatic 'sdictInt) `bindCP` cpSend $(mkStatic 'sdictInt) us
289308 send them (1234 :: Int)
290309 (1234 :: Int) <- expect
291− return ()
310+ liftIO $ putMVar done ()
311+ takeMVar done
292312
293313testSpawnChannel :: Transport -> RemoteTable -> IO ()
294314testSpawnChannel transport rtable = do
315+ done <- newEmptyMVar
295316 [node1, node2] <- replicateM 2 $ newLocalNode transport rtable
296317
297− runProcess node1 $ do
318+ forkProcess node1 $ do
298319 pingServer <- spawnChannel
299320 (sdictSendPort sdictUnit)
300321 (localNodeId node2)
@@ -302,13 +323,18 @@ testSpawnChannel transport rtable = do
302323 (sendReply, receiveReply) <- newChan
303324 sendChan pingServer sendReply
304325 receiveChan receiveReply
326+ liftIO $ putMVar done ()
327+
328+ takeMVar done
305329
306330testTDict :: Transport -> RemoteTable -> IO ()
307331testTDict transport rtable = do
332+ done <- newEmptyMVar
308333 [node1, node2] <- replicateM 2 $ newLocalNode transport rtable
309− runProcess node1 $ do
334+ forkProcess node1 $ do
310335 True <- call $(functionTDict 'isPrime) (localNodeId node2) ($(mkClosure 'isPrime) (79 :: Integer))
311− return ()
336+ liftIO $ putMVar done ()
337+ takeMVar done
312338
313339main :: IO ()
314340main = do
distributed-static/LICENSEnew file+30
@@ -0,0 +1,30 @@
1+Copyright (c) 2012, Edsko de Vries
2+
3+All rights reserved.
4+
5+Redistribution and use in source and binary forms, with or without
6+modification, are permitted provided that the following conditions are met:
7+
8+ * Redistributions of source code must retain the above copyright
9+ notice, this list of conditions and the following disclaimer.
10+
11+ * Redistributions in binary form must reproduce the above
12+ copyright notice, this list of conditions and the following
13+ disclaimer in the documentation and/or other materials provided
14+ with the distribution.
15+
16+ * Neither the name of Edsko de Vries nor the names of other
17+ contributors may be used to endorse or promote products derived
18+ from this software without specific prior written permission.
19+
20+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
distributed-static/Setup.hsnew file+2
@@ -0,0 +1,2 @@
1+import Distribution.Simple
2+main = defaultMain
distributed-static/distributed-static.cabalnew file+37
@@ -0,0 +1,37 @@
1+Name: distributed-static
2+Version: 0.1.0.0
3+Synopsis: Compositional, type-safe, polymorphic static values and closures
4+Description: /Towards Haskell in the Cloud/ (Epstein et al, Haskell
5+ Symposium 2011) introduces the concept of /static/ values:
6+ values that are known at compile time. In a distributed
7+ setting where all nodes are running the same executable,
8+ static values can be serialized simply by transmitting a
9+ code pointer to the value. This however requires special
10+ compiler support, which is not yet available in ghc. We
11+ can mimick the behaviour by keeping an explicit mapping
12+ ('RemoteTable') from labels to values (and making sure
13+ that all distributed nodes are using the same
14+ 'RemoteTable'). In this module we implement this mimickry
15+ and various extensions: type safety (including for
16+ polymorphic static values) and compositionality.
17+Homepage: http://www.github.com/haskell-distributed/distributed-process
18+License: BSD3
19+License-File: LICENSE
20+Author: Edsko de Vries
21+Maintainer: edsko@well-typed.com
22+Copyright: Well-Typed LLP
23+Category: Control
24+Build-Type: Simple
25+Cabal-Version: >=1.8
26+
27+Library
28+ Exposed-Modules: Control.Distributed.Static
29+ Build-Depends: base >= 4 && < 5,
30+ rank1dynamic >= 0.1 && < 0.2,
31+ containers >= 0.4 && < 0.5,
32+ bytestring >= 0.9 && < 0.11,
33+ binary >= 0.5 && < 0.6
34+ HS-Source-Dirs: src
35+ Extensions: DeriveDataTypeable,
36+ ScopedTypeVariables
37+ GHC-Options: -Wall
distributed-static/src/Control/Distributed/Static.hsnew file+435
@@ -0,0 +1,435 @@
1+-- | /Towards Haskell in the Cloud/ (Epstein et al, Haskell Symposium 2011)
2+-- introduces the concept of /static/ values: values that are known at compile
3+-- time. In a distributed setting where all nodes are running the same
4+-- executable, static values can be serialized simply by transmitting a code
5+-- pointer to the value. This however requires special compiler support, which
6+-- is not yet available in ghc. We can mimick the behaviour by keeping an
7+-- explicit mapping ('RemoteTable') from labels to values (and making sure that
8+-- all distributed nodes are using the same 'RemoteTable'). In this module
9+-- we implement this mimickry and various extensions.
10+--
11+-- [Dynamic type checking]
12+--
13+-- The paper stipulates that 'Static' values should have a free 'Binary'
14+-- instance:
15+--
16+-- > instance Binary (Static a)
17+--
18+-- This however is not (runtime) type safe: for instance, what would be the
19+-- behaviour of
20+--
21+-- > f :: Static Int -> Static Bool
22+-- > f = decode . encode
23+--
24+-- For this reason we work only with 'Typeable' terms in this module, and
25+-- implement runtime checks
26+--
27+-- > instance Typeable a => Binary (Static a)
28+--
29+-- The above function 'f' typechecks but throws an exception if executed. The
30+-- type representation we use, however, is not the standard
31+-- 'Data.Typeable.TypeRep' from "Data.Typeable" but
32+-- 'Data.Rank1Typeable.TypeRep' from "Data.Rank1Typeable". This means that we
33+-- can represent polymorphic static values (see below for an example).
34+--
35+-- Since the runtime mapping ('RemoteTable') contains values of different types,
36+-- it maps labels ('String's) to 'Data.Rank1Dynamic.Dynamic' values. Again, we
37+-- use the implementation from "Data.Rank1Dynamic" so that we can store
38+-- polymorphic dynamic values.
39+--
40+-- [Compositionality]
41+--
42+-- Static values as described in the paper are not compositional: there is no
43+-- way to combine two static values and get a static value out of it. This
44+-- makes sense when interpreting static strictly as /known at compile time/,
45+-- but it severely limits expressiveness. However, the main motivation for
46+-- 'static' is not that they are known at compile time but rather that
47+-- /they provide a free/ 'Binary' /instance/. We therefore provide two basic
48+-- constructors for 'Static' values:
49+--
50+-- > staticLabel :: String -> Static a
51+-- > staticApply :: Static (a -> b) -> Static a -> Static b
52+--
53+-- The first constructor refers to a label in a 'RemoteTable'. The second
54+-- allows to apply a static function to a static argument, and makes 'Static'
55+-- compositional: once we have 'staticApply' we can implement numerous derived
56+-- combinators on 'Static' values (we define a few in this module; see
57+-- 'staticCompose', 'staticSplit', and 'staticConst').
58+--
59+-- [Closures]
60+--
61+-- Closures in functional programming arise when we partially apply a function.
62+-- A closure is a code pointer together with a runtime data structure that
63+-- represents the value of the free variables of the function. A 'Closure'
64+-- represents these closures explicitly so that they can be serialized:
65+--
66+-- > data Closure a = Closure (Static (ByteString -> a)) ByteString
67+--
68+-- See /Towards Haskell in the Cloud/ for the rationale behind representing
69+-- the function closure environment in serialized ('ByteString') form. Any
70+-- static value can trivially be turned into a 'Closure' ('staticClosure').
71+-- Moreover, since 'Static' is now compositional, we can also define derived
72+-- operators on 'Closure' values ('closureApplyStatic', 'closureApply',
73+-- 'closureCompose', 'closureSplit').
74+--
75+-- [Monomorphic example]
76+--
77+-- Suppose we are working in the context of some distributed environment, with
78+-- a monadic type 'Process' representing processes, 'NodeId' representing node
79+-- addresses and 'ProcessId' representing process addresses. Suppose further
80+-- that we have a primitive
81+--
82+-- > sendInt :: ProcessId -> Int -> Process ()
83+--
84+-- We might want to define
85+--
86+-- > sendIntClosure :: ProcessId -> Closure (Int -> Process ())
87+--
88+-- In order to do that, we need a static version of 'send', and a static
89+-- decoder for 'ProcessId':
90+--
91+-- > sendIntStatic :: Static (ProcessId -> Int -> Process ())
92+-- > sendIntStatic = staticLabel "$send"
93+--
94+-- > decodeProcessIdStatic :: Static (ByteString -> Int)
95+-- > decodeProcessIdStatic = staticLabel "$decodeProcessId"
96+--
97+-- where of course we have to make sure to use an appropriate 'RemoteTable':
98+--
99+-- > rtable :: RemoteTable
100+-- > rtable = registerStatic "$send" (toDynamic sendInt)
101+-- > . registerStatic "$decodeProcessId" (toDynamic (decode :: ByteString -> Int))
102+-- > $ initRemoteTable
103+--
104+-- We can now define 'sendIntClosure':
105+--
106+-- > sendIntClosure :: ProcessId -> Closure (Int -> Process ())
107+-- > sendIntClosure pid = Closure decoder (encode pid)
108+-- > where
109+-- > decoder :: Static (ByteString -> Int -> Process ())
110+-- > decoder = sendIntStatic `staticCompose` decodeProcessIdStatic
111+--
112+-- [Polymorphic example]
113+--
114+-- Suppose we wanted to define a primitive
115+--
116+-- > sendIntResult :: ProcessId -> Closure (Process Int) -> Closure (Process ())
117+--
118+-- which turns a process that computes an integer into a process that computes
119+-- the integer and then sends it someplace else.
120+--
121+-- We can define
122+--
123+-- > bindStatic :: (Typeable a, Typeable b) => Static (Process a -> (a -> Process b) -> Process b)
124+-- > bindStatic = staticLabel "$bind"
125+--
126+-- provided that we register this label:
127+--
128+-- > rtable :: RemoteTable
129+-- > rtable = ...
130+-- > . registerStatic "$bind" ((>>=) :: Process ANY1 -> (ANY1 -> Process ANY2) -> Process ANY2)
131+-- > $ initRemoteTable
132+--
133+-- (Note that we are using the special 'Data.Rank1Typeable.ANY1' and
134+-- 'Data.Rank1Typeable.ANY2' types from "Data.Rank1Typeable" to represent this
135+-- polymorphic value.) Once we have a static bind we can define
136+--
137+-- > sendIntResult :: ProcessId -> Closure (Process Int) -> Closure (Process ())
138+-- > sendIntResult pid cl = bindStatic `closureApplyStatic` cl `closureApply` sendIntClosure pid
139+--
140+-- [Dealing with qualified types]
141+--
142+-- In the above we were careful to avoid qualified types. Suppose that we have
143+-- instead
144+--
145+-- > send :: Binary a => ProcessId -> a -> Process ()
146+--
147+-- If we now want to define 'sendClosure', analogous to 'sendIntClosure' above,
148+-- we somehow need to include the 'Binary' instance in the closure -- after
149+-- all, we can ship this closure someplace else, where it needs to accept an
150+-- 'a', /then encode it/, and send it off. In order to do this, we need to turn
151+-- the Binary instance into an explicit dictionary:
152+--
153+-- > data BinaryDict a where
154+-- > BinaryDict :: Binary a => BinaryDict a
155+-- >
156+-- > sendDict :: BinaryDict a -> ProcessId -> a -> Process ()
157+-- > sendDict BinaryDict = send
158+--
159+-- Now 'sendDict' is a normal polymorphic value:
160+--
161+-- > sendDictStatic :: Static (BinaryDict a -> ProcessId -> a -> Process ())
162+-- > sendDictStatic = staticLabel "$sendDict"
163+-- >
164+-- > rtable :: RemoteTable
165+-- > rtable = ...
166+-- > . registerStatic "$sendDict" (sendDict :: BinaryDict ANY -> ProcessId -> ANY -> Process ())
167+-- > $ initRemoteTable
168+--
169+-- so that we can define
170+--
171+-- > sendClosure :: Static (BinaryDict a) -> Process a -> Closure (a -> Process ())
172+-- > sendClosure dict pid = Closure decoder (encode pid)
173+-- > where
174+-- > decoder :: Static (ByteString -> a -> Process ())
175+-- > decoder = (sendDictStatic `staticApply` dict) `staticCompose` decodeProcessIdStatic
176+--
177+-- [Word of Caution]
178+--
179+-- You should not /define/ functions on 'ANY' and co. For example, the following
180+-- definition of 'rtable' is incorrect:
181+--
182+-- > rtable :: RemoteTable
183+-- > rtable = registerStatic "$sdictSendPort" sdictSendPort
184+-- > $ initRemoteTable
185+-- > where
186+-- > sdictSendPort :: SerializableDict ANY -> SerializableDict (SendPort ANY)
187+-- > sdictSendPort SerializableDict = SerializableDict
188+--
189+-- This definition of 'sdictSendPort' ignores its argument completely, and
190+-- constructs a 'SerializableDict' for the /monomorphic/ type @SendPort ANY@,
191+-- which isn't what you want. Instead, you should do
192+--
193+-- > rtable :: RemoteTable
194+-- > rtable = registerStatic "$sdictSendPort" (sdictSendPort :: SerializableDict ANY -> SerializableDict (SendPort ANY))
195+-- > $ initRemoteTable
196+-- > where
197+-- > sdictSendPort :: forall a. SerializableDict a -> SerializableDict (SendPort a)
198+-- > sdictSendPort SerializableDict = SerializableDict
199+module Control.Distributed.Static
200+ ( -- * Static values
201+ Static
202+ , staticLabel
203+ , staticApply
204+ -- * Derived static combinators
205+ , staticCompose
206+ , staticSplit
207+ , staticConst
208+ -- * Closures
209+ , Closure(Closure)
210+ -- * Derived closure combinators
211+ , staticClosure
212+ , closureApplyStatic
213+ , closureApply
214+ , closureCompose
215+ , closureSplit
216+ -- * Resolution
217+ , RemoteTable
218+ , initRemoteTable
219+ , registerStatic
220+ , unstatic
221+ , unclosure
222+ ) where
223+
224+import Data.Binary
225+ ( Binary(get, put)
226+ , Put
227+ , Get
228+ , putWord8
229+ , getWord8
230+ , encode
231+ , decode
232+ )
233+import Data.ByteString.Lazy (ByteString, empty)
234+import Data.Map (Map)
235+import qualified Data.Map as Map (lookup, empty, insert)
236+import Control.Applicative ((<$>), (<*>))
237+import Control.Arrow as Arrow ((***), app)
238+import Data.Rank1Dynamic (Dynamic, toDynamic, fromDynamic, dynApply)
239+import Data.Rank1Typeable
240+ ( Typeable
241+ , typeOf
242+ , ANY1
243+ , ANY2
244+ , ANY3
245+ , ANY4
246+ , isInstanceOf
247+ )
248+
249+--------------------------------------------------------------------------------
250+-- Introducing static values --
251+--------------------------------------------------------------------------------
252+
253+data StaticLabel =
254+ StaticLabel String
255+ | StaticApply StaticLabel StaticLabel
256+ deriving (Typeable, Show)
257+
258+-- | A static value. Static is opaque; see 'staticLabel' and 'staticApply'.
259+newtype Static a = Static StaticLabel
260+ deriving (Typeable, Show)
261+
262+instance Typeable a => Binary (Static a) where
263+ put (Static label) = putStaticLabel label >> put (typeOf (undefined :: a))
264+ get = do
265+ label <- getStaticLabel
266+ typeRep <- get
267+ case typeOf (undefined :: a) `isInstanceOf` typeRep of
268+ Left err -> fail $ "Static.get: type error: " ++ err
269+ Right () -> return (Static label)
270+
271+-- We don't want StaticLabel to be its own Binary instance
272+putStaticLabel :: StaticLabel -> Put
273+putStaticLabel (StaticLabel string) =
274+ putWord8 0 >> put string
275+putStaticLabel (StaticApply label1 label2) =
276+ putWord8 1 >> putStaticLabel label1 >> putStaticLabel label2
277+
278+getStaticLabel :: Get StaticLabel
279+getStaticLabel = do
280+ header <- getWord8
281+ case header of
282+ 0 -> StaticLabel <$> get
283+ 1 -> StaticApply <$> getStaticLabel <*> getStaticLabel
284+ _ -> fail "StaticLabel.get: invalid"
285+
286+-- | Create a primitive static value.
287+--
288+-- It is the responsibility of the client code to make sure the corresponding
289+-- entry in the 'RemoteTable' has the appropriate type.
290+staticLabel :: String -> Static a
291+staticLabel = Static . StaticLabel
292+
293+-- | Apply two static values
294+staticApply :: Static (a -> b) -> Static a -> Static b
295+staticApply (Static f) (Static x) = Static (StaticApply f x)
296+
297+--------------------------------------------------------------------------------
298+-- Eliminating static values --
299+--------------------------------------------------------------------------------
300+
301+-- | Runtime dictionary for 'unstatic' lookups
302+newtype RemoteTable = RemoteTable (Map String Dynamic)
303+
304+-- | Initial remote table
305+initRemoteTable :: RemoteTable
306+initRemoteTable =
307+ registerStatic "$compose" (toDynamic ((.) :: (ANY2 -> ANY3) -> (ANY1 -> ANY2) -> ANY1 -> ANY3))
308+ . registerStatic "$const" (toDynamic (const :: ANY1 -> ANY2 -> ANY1))
309+ . registerStatic "$split" (toDynamic ((***) :: (ANY1 -> ANY3) -> (ANY2 -> ANY4) -> (ANY1, ANY2) -> (ANY3, ANY4)))
310+ . registerStatic "$app" (toDynamic (app :: (ANY1 -> ANY2, ANY1) -> ANY2))
311+ . registerStatic "$decodeEnvPair" (toDynamic (decode :: ByteString -> (ByteString, ByteString)))
312+ $ RemoteTable Map.empty
313+
314+-- | Register a static label
315+registerStatic :: String -> Dynamic -> RemoteTable -> RemoteTable
316+registerStatic label dyn (RemoteTable rtable)
317+ = RemoteTable (Map.insert label dyn rtable)
318+
319+-- Pseudo-type: RemoteTable -> Static a -> a
320+resolveStaticLabel :: RemoteTable -> StaticLabel -> Either String Dynamic
321+resolveStaticLabel (RemoteTable rtable) (StaticLabel label) =
322+ case Map.lookup label rtable of
323+ Nothing -> Left $ "Invalid static label '" ++ label ++ "'"
324+ Just d -> Right d
325+resolveStaticLabel rtable (StaticApply label1 label2) = do
326+ f <- resolveStaticLabel rtable label1
327+ x <- resolveStaticLabel rtable label2
328+ f `dynApply` x
329+
330+-- | Resolve a static value
331+unstatic :: Typeable a => RemoteTable -> Static a -> Either String a
332+unstatic rtable (Static static) = do
333+ dyn <- resolveStaticLabel rtable static
334+ fromDynamic dyn
335+
336+--------------------------------------------------------------------------------
337+-- Closures --
338+--------------------------------------------------------------------------------
339+
340+-- | A closure is a static value and an encoded environment
341+data Closure a = Closure (Static (ByteString -> a)) ByteString
342+ deriving (Typeable, Show)
343+
344+instance Typeable a => Binary (Closure a) where
345+ put (Closure static env) = put static >> put env
346+ get = Closure <$> get <*> get
347+
348+-- | Resolve a closure
349+unclosure :: Typeable a => RemoteTable -> Closure a -> Either String a
350+unclosure rtable (Closure static env) = do
351+ f <- unstatic rtable static
352+ return (f env)
353+
354+-- | Convert a static value into a closure.
355+staticClosure :: Typeable a => Static a -> Closure a
356+staticClosure static = Closure (staticConst static) empty
357+
358+--------------------------------------------------------------------------------
359+-- Predefined static values --
360+--------------------------------------------------------------------------------
361+
362+-- | Static version of ('Prelude..')
363+composeStatic :: (Typeable a, Typeable b, Typeable c)
364+ => Static ((b -> c) -> (a -> b) -> a -> c)
365+composeStatic = staticLabel "$compose"
366+
367+-- | Static version of 'const'
368+constStatic :: (Typeable a, Typeable b)
369+ => Static (a -> b -> a)
370+constStatic = staticLabel "$const"
371+
372+-- | Static version of ('Arrow.***')
373+splitStatic :: (Typeable a, Typeable a', Typeable b, Typeable b')
374+ => Static ((a -> b) -> (a' -> b') -> (a, a') -> (b, b'))
375+splitStatic = staticLabel "$split"
376+
377+-- | Static version of 'Arrow.app'
378+appStatic :: (Typeable a, Typeable b)
379+ => Static ((a -> b, a) -> b)
380+appStatic = staticLabel "$app"
381+
382+--------------------------------------------------------------------------------
383+-- Combinators on static values --
384+--------------------------------------------------------------------------------
385+
386+-- | Static version of ('Prelude..')
387+staticCompose :: (Typeable a, Typeable b, Typeable c)
388+ => Static (b -> c) -> Static (a -> b) -> Static (a -> c)
389+staticCompose g f = composeStatic `staticApply` g `staticApply` f
390+
391+-- | Static version of ('Control.Arrow.***')
392+staticSplit :: (Typeable a, Typeable a', Typeable b, Typeable b')
393+ => Static (a -> b) -> Static (a' -> b') -> Static ((a, a') -> (b, b'))
394+staticSplit f g = splitStatic `staticApply` f `staticApply` g
395+
396+-- | Static version of 'Prelude.const'
397+staticConst :: (Typeable a, Typeable b)
398+ => Static a -> Static (b -> a)
399+staticConst x = constStatic `staticApply` x
400+
401+--------------------------------------------------------------------------------
402+-- Combinators on Closures --
403+--------------------------------------------------------------------------------
404+
405+-- | Apply a static function to a closure
406+closureApplyStatic :: (Typeable a, Typeable b)
407+ => Static (a -> b) -> Closure a -> Closure b
408+closureApplyStatic f (Closure decoder env) =
409+ Closure (f `staticCompose` decoder) env
410+
411+decodeEnvPairStatic :: Static (ByteString -> (ByteString, ByteString))
412+decodeEnvPairStatic = staticLabel "$decodeEnvPair"
413+
414+-- | Closure application
415+closureApply :: forall a b. (Typeable a, Typeable b)
416+ => Closure (a -> b) -> Closure a -> Closure b
417+closureApply (Closure fdec fenv) (Closure xdec xenv) =
418+ Closure decoder (encode (fenv, xenv))
419+ where
420+ decoder :: Static (ByteString -> b)
421+ decoder = appStatic
422+ `staticCompose`
423+ (fdec `staticSplit` xdec)
424+ `staticCompose`
425+ decodeEnvPairStatic
426+
427+-- | Closure composition
428+closureCompose :: (Typeable a, Typeable b, Typeable c)
429+ => Closure (b -> c) -> Closure (a -> b) -> Closure (a -> c)
430+closureCompose g f = composeStatic `closureApplyStatic` g `closureApply` f
431+
432+-- | Closure version of ('Arrow.***')
433+closureSplit :: (Typeable a, Typeable a', Typeable b, Typeable b')
434+ => Closure (a -> b) -> Closure (a' -> b') -> Closure ((a, a') -> (b, b'))
435+closureSplit f g = splitStatic `closureApplyStatic` f `closureApply` g
rank1dynamic/LICENSEnew file+30
@@ -0,0 +1,30 @@
1+Copyright (c) 2012, Edsko de Vries
2+
3+All rights reserved.
4+
5+Redistribution and use in source and binary forms, with or without
6+modification, are permitted provided that the following conditions are met:
7+
8+ * Redistributions of source code must retain the above copyright
9+ notice, this list of conditions and the following disclaimer.
10+
11+ * Redistributions in binary form must reproduce the above
12+ copyright notice, this list of conditions and the following
13+ disclaimer in the documentation and/or other materials provided
14+ with the distribution.
15+
16+ * Neither the name of Edsko de Vries nor the names of other
17+ contributors may be used to endorse or promote products derived
18+ from this software without specific prior written permission.
19+
20+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
rank1dynamic/Setup.hsnew file+2
@@ -0,0 +1,2 @@
1+import Distribution.Simple
2+main = defaultMain
rank1dynamic/rank1dynamic.cabalnew file+28
@@ -0,0 +1,28 @@
1+Name: rank1dynamic
2+Version: 0.1.0.0
3+Synopsis: Like Data.Dynamic/Data.Typeable but with support for rank-1 polymorphic types
4+Description: "Data.Typeable" and "Data.Dynamic" only support monomorphic types.
5+ In this package we provide similar functionality but with
6+ support for rank-1 polymorphic types.
7+Homepage: http://github.com/haskell-distributed/distributed-process
8+License: BSD3
9+License-File: LICENSE
10+Author: Edsko de Vries
11+Maintainer: edsko@well-typed.com
12+Copyright: Well-Typed LLP
13+Category: Data
14+Build-Type: Simple
15+Cabal-Version: >=1.8
16+
17+Library
18+ Exposed-Modules: Data.Rank1Dynamic,
19+ Data.Rank1Typeable
20+ Build-Depends: base ==4.5.*,
21+ ghc-prim >= 0.2 && < 0.3,
22+ binary >= 0.5 && < 0.6
23+ HS-Source-Dirs: src
24+ GHC-Options: -Wall
25+ Extensions: EmptyDataDecls,
26+ DeriveDataTypeable,
27+ ViewPatterns,
28+ CPP
rank1dynamic/src/Data/Rank1Dynamic.hsnew file+120
@@ -0,0 +1,120 @@
1+-- | Dynamic values with support for rank-1 polymorphic types.
2+--
3+-- [Examples of fromDynamic]
4+--
5+-- These examples correspond to the 'Data.Rank1Typeable.isInstanceOf' examples
6+-- in "Data.Rank1Typeable".
7+--
8+-- > > do f <- fromDynamic (toDynamic (even :: Int -> Bool)) ; return $ (f :: Int -> Int) 0
9+-- > Left "Cannot unify Int and Bool"
10+-- >
11+-- > > do f <- fromDynamic (toDynamic (const 1 :: ANY -> Int)) ; return $ (f :: Int -> Int) 0
12+-- > Right 1
13+-- >
14+-- > > do f <- fromDynamic (toDynamic (unsafeCoerce :: ANY1 -> ANY2)) ; return $ (f :: Int -> Int) 0
15+-- > Right 0
16+-- >
17+-- > > do f <- fromDynamic (toDynamic (id :: ANY -> ANY)) ; return $ (f :: Int -> Bool) 0
18+-- > Left "Cannot unify Bool and Int"
19+-- >
20+-- > > do f <- fromDynamic (toDynamic (undefined :: ANY)) ; return $ (f :: Int -> Int) 0
21+-- > Right *** Exception: Prelude.undefined
22+-- >
23+-- > > do f <- fromDynamic (toDynamic (id :: ANY -> ANY)) ; return $ (f :: Int)
24+-- > Left "Cannot unify Int and ->"
25+--
26+-- [Examples of dynApply]
27+--
28+-- These examples correspond to the 'Data.Rank1Typeable.funResultTy' examples
29+-- in "Data.Rank1Typeable".
30+--
31+-- > > do app <- toDynamic (id :: ANY -> ANY) `dynApply` toDynamic True ; f <- fromDynamic app ; return $ (f :: Bool)
32+-- > Right True
33+-- >
34+-- > > do app <- toDynamic (const :: ANY -> ANY1 -> ANY) `dynApply` toDynamic True ; f <- fromDynamic app ; return $ (f :: Int -> Bool) 0
35+-- > Right True
36+-- >
37+-- > > do app <- toDynamic (($ True) :: (Bool -> ANY) -> ANY) `dynApply` toDynamic (id :: ANY -> ANY) ; f <- fromDynamic app ; return (f :: Bool)
38+-- > Right True
39+-- >
40+-- > > app <- toDynamic (const :: ANY -> ANY1 -> ANY) `dynApply` toDynamic (id :: ANY -> ANY) ; f <- fromDynamic app ; return $ (f :: Int -> Bool -> Bool) 0 True
41+-- > Right True
42+-- >
43+-- > > do app <- toDynamic ((\f -> f . f) :: (ANY -> ANY) -> ANY -> ANY) `dynApply` toDynamic (even :: Int -> Bool) ; f <- fromDynamic app ; return (f :: ())
44+-- > Left "Cannot unify Int and Bool"
45+--
46+-- [Using toDynamic]
47+--
48+-- When using polymorphic values you need to give an explicit type annotation:
49+--
50+-- > > toDynamic id
51+-- >
52+-- > <interactive>:46:1:
53+-- > Ambiguous type variable `a0' in the constraint:
54+-- > (Typeable a0) arising from a use of `toDynamic'
55+-- > Probable fix: add a type signature that fixes these type variable(s)
56+-- > In the expression: toDynamic id
57+-- > In an equation for `it': it = toDynamic id
58+--
59+-- versus
60+--
61+-- > > toDynamic (id :: ANY -> ANY)
62+-- > <<ANY -> ANY>>
63+--
64+-- Note that these type annotation are checked by ghc like any other:
65+--
66+-- > > toDynamic (id :: ANY -> ANY1)
67+-- >
68+-- > <interactive>:45:12:
69+-- > Couldn't match expected type `V1' with actual type `V0'
70+-- > Expected type: ANY -> ANY1
71+-- > Actual type: ANY -> ANY
72+-- > In the first argument of `toDynamic', namely `(id :: ANY -> ANY1)'
73+-- > In the expression: toDynamic (id :: ANY -> ANY1)
74+module Data.Rank1Dynamic
75+ ( Dynamic
76+ , toDynamic
77+ , fromDynamic
78+ , TypeError
79+ , dynTypeRep
80+ , dynApply
81+ ) where
82+
83+import qualified GHC.Prim as GHC (Any)
84+import Data.Rank1Typeable
85+ ( Typeable
86+ , TypeRep
87+ , typeOf
88+ , isInstanceOf
89+ , TypeError
90+ , funResultTy
91+ )
92+import Unsafe.Coerce (unsafeCoerce)
93+
94+-- | Encapsulate an object and its type
95+data Dynamic = Dynamic TypeRep GHC.Any
96+
97+instance Show Dynamic where
98+ showsPrec _ (Dynamic t _) = showString "<<" . shows t . showString ">>"
99+
100+-- | Introduce a dynamic value
101+toDynamic :: Typeable a => a -> Dynamic
102+toDynamic x = Dynamic (typeOf x) (unsafeCoerce x)
103+
104+-- | Eliminate a dynamic value
105+fromDynamic :: Typeable a => Dynamic -> Either TypeError a
106+fromDynamic (Dynamic t v) =
107+ case unsafeCoerce v of
108+ r -> case typeOf r `isInstanceOf` t of
109+ Left err -> Left err
110+ Right () -> Right r
111+
112+-- | Apply one dynamic value to another
113+dynApply :: Dynamic -> Dynamic -> Either TypeError Dynamic
114+dynApply (Dynamic t1 f) (Dynamic t2 x) = do
115+ t3 <- funResultTy t1 t2
116+ return $ Dynamic t3 (unsafeCoerce f x)
117+
118+-- | The type representation of a dynamic value
119+dynTypeRep :: Dynamic -> TypeRep
120+dynTypeRep (Dynamic t _) = t
rank1dynamic/src/Data/Rank1Typeable.hsnew file+362
@@ -0,0 +1,362 @@
1+-- | Runtime type representation of terms with support for rank-1 polymorphic
2+-- types with type variables of kind *.
3+--
4+-- The essence of this module is that we use the standard 'Typeable'
5+-- representation of "Data.Typeable" but we introduce a special (empty) data
6+-- type 'TypVar' which represents type variables. 'TypVar' is indexed by an
7+-- arbitrary other data type, giving you an unbounded number of type variables;
8+-- for convenience, we define 'ANY', 'ANY1', .., 'ANY9'.
9+--
10+-- [Examples of isInstanceOf]
11+--
12+-- > -- We CANNOT use a term of type 'Int -> Bool' as 'Int -> Int'
13+-- > > typeOf (undefined :: Int -> Int) `isInstanceOf` typeOf (undefined :: Int -> Bool)
14+-- > Left "Cannot unify Int and Bool"
15+-- >
16+-- > -- We CAN use a term of type 'forall a. a -> Int' as 'Int -> Int'
17+-- > > typeOf (undefined :: Int -> Int) `isInstanceOf` typeOf (undefined :: ANY -> Int)
18+-- > Right ()
19+-- >
20+-- > -- We CAN use a term of type 'forall a b. a -> b' as 'forall a. a -> a'
21+-- > > typeOf (undefined :: ANY -> ANY) `isInstanceOf` typeOf (undefined :: ANY -> ANY1)
22+-- > Right ()
23+-- >
24+-- > -- We CANNOT use a term of type 'forall a. a -> a' as 'forall a b. a -> b'
25+-- > > typeOf (undefined :: ANY -> ANY1) `isInstanceOf` typeOf (undefined :: ANY -> ANY)
26+-- > Left "Cannot unify Succ and Zero"
27+-- >
28+-- > -- We CAN use a term of type 'forall a. a' as 'forall a. a -> a'
29+-- > > typeOf (undefined :: ANY -> ANY) `isInstanceOf` typeOf (undefined :: ANY)
30+-- > Right ()
31+-- >
32+-- > -- We CANNOT use a term of type 'forall a. a -> a' as 'forall a. a'
33+-- > > typeOf (undefined :: ANY) `isInstanceOf` typeOf (undefined :: ANY -> ANY)
34+-- > Left "Cannot unify Skolem and ->"
35+--
36+-- (Admittedly, the quality of the type errors could be improved.)
37+--
38+-- [Examples of funResultTy]
39+--
40+-- > -- Apply fn of type (forall a. a -> a) to arg of type Bool gives Bool
41+-- > > funResultTy (typeOf (undefined :: ANY -> ANY)) (typeOf (undefined :: Bool))
42+-- > Right Bool
43+-- >
44+-- > -- Apply fn of type (forall a b. a -> b -> a) to arg of type Bool gives forall a. a -> Bool
45+-- > > funResultTy (typeOf (undefined :: ANY -> ANY1 -> ANY)) (typeOf (undefined :: Bool))
46+-- > Right (ANY -> Bool) -- forall a. a -> Bool
47+-- >
48+-- > -- Apply fn of type (forall a. (Bool -> a) -> a) to argument of type (forall a. a -> a) gives Bool
49+-- > > funResultTy (typeOf (undefined :: (Bool -> ANY) -> ANY)) (typeOf (undefined :: ANY -> ANY))
50+-- > Right Bool
51+-- >
52+-- > -- Apply fn of type (forall a b. a -> b -> a) to arg of type (forall a. a -> a) gives (forall a b. a -> b -> b)
53+-- > > funResultTy (typeOf (undefined :: ANY -> ANY1 -> ANY)) (typeOf (undefined :: ANY1 -> ANY1))
54+-- > Right (ANY -> ANY1 -> ANY1)
55+-- >
56+-- > -- Cannot apply function of type (forall a. (a -> a) -> a -> a) to arg of type (Int -> Bool)
57+-- > > funResultTy (typeOf (undefined :: (ANY -> ANY) -> (ANY -> ANY))) (typeOf (undefined :: Int -> Bool))
58+-- > Left "Cannot unify Int and Bool"
59+module Data.Rank1Typeable
60+ ( -- * Basic types
61+ TypeRep
62+ , typeOf
63+ , splitTyConApp
64+ , mkTyConApp
65+ , underlyingTypeRep
66+ -- * Operations on type representations
67+ , isInstanceOf
68+ , funResultTy
69+ , TypeError
70+ -- * Type variables
71+ , TypVar
72+ , Zero
73+ , Succ
74+ , V0
75+ , V1
76+ , V2
77+ , V3
78+ , V4
79+ , V5
80+ , V6
81+ , V7
82+ , V8
83+ , V9
84+ , ANY
85+ , ANY1
86+ , ANY2
87+ , ANY3
88+ , ANY4
89+ , ANY5
90+ , ANY6
91+ , ANY7
92+ , ANY8
93+ , ANY9
94+ -- * Re-exports from Typeable
95+ , Typeable
96+ ) where
97+
98+import Prelude hiding (succ)
99+import Control.Arrow ((***), second)
100+import Control.Monad (void)
101+import Control.Applicative ((<$>))
102+import Data.List (intersperse, isPrefixOf)
103+import Data.Maybe (fromMaybe)
104+import Data.Typeable (Typeable, tyConName, mkTyCon3)
105+import Data.Typeable.Internal (listTc, funTc, TyCon(TyCon))
106+import Data.Binary (Binary(get, put))
107+import GHC.Fingerprint.Type (Fingerprint(..))
108+import qualified Data.Typeable as Typeable
109+ ( TypeRep
110+ , typeOf
111+ , splitTyConApp
112+ , mkTyConApp
113+ )
114+
115+--------------------------------------------------------------------------------
116+-- The basic type --
117+--------------------------------------------------------------------------------
118+
119+-- | Dynamic type representation with support for rank-1 types
120+newtype TypeRep = TypeRep {
121+ -- | Return the underlying standard ("Data.Typeable") type representation
122+ underlyingTypeRep :: Typeable.TypeRep
123+ }
124+
125+-- | Compare two type representations
126+--
127+-- For base >= 4.6 this compares fingerprints, but older versions of base
128+-- have a bug in the fingerprint construction
129+-- (<http://hackage.haskell.org/trac/ghc/ticket/5962>)
130+instance Eq TypeRep where
131+#if ! MIN_VERSION_base(4,6,0)
132+ (splitTyConApp -> (c1, ts1)) == (splitTyConApp -> (c2, ts2)) =
133+ c1 == c2 && all (uncurry (==)) (zip ts1 ts2)
134+#else
135+ t1 == t2 = underlyingTypeRep t1 == underlyingTypeRep t2
136+#endif
137+
138+-- Binary instance for 'TypeRep', avoiding orphan instances
139+instance Binary TypeRep where
140+ put (splitTyConApp -> (TyCon (Fingerprint hi lo) package modul name, ts)) = do
141+ put hi
142+ put lo
143+ put package
144+ put modul
145+ put name
146+ put ts
147+ get = do
148+ hi <- get
149+ lo <- get
150+ package <- get
151+ modul <- get
152+ name <- get
153+ ts <- get
154+ return $ mkTyConApp (TyCon (Fingerprint hi lo) package modul name) ts
155+
156+-- | The type representation of any 'Typeable' term
157+typeOf :: Typeable a => a -> TypeRep
158+typeOf = TypeRep . Typeable.typeOf
159+
160+--------------------------------------------------------------------------------
161+-- Constructors/destructors (views) --
162+--------------------------------------------------------------------------------
163+
164+-- | Split a type representation into the application of
165+-- a type constructor and its argument
166+splitTyConApp :: TypeRep -> (TyCon, [TypeRep])
167+splitTyConApp t =
168+ let (c, ts) = Typeable.splitTyConApp (underlyingTypeRep t)
169+ in (c, map TypeRep ts)
170+
171+-- | Inverse of 'splitTyConApp'
172+mkTyConApp :: TyCon -> [TypeRep] -> TypeRep
173+mkTyConApp c ts
174+ = TypeRep (Typeable.mkTyConApp c (map underlyingTypeRep ts))
175+
176+isTypVar :: TypeRep -> Maybe Var
177+isTypVar (splitTyConApp -> (c, [t])) | c == typVar = Just t
178+isTypVar _ = Nothing
179+
180+mkTypVar :: Var -> TypeRep
181+mkTypVar x = mkTyConApp typVar [x]
182+
183+typVar :: TyCon
184+typVar = let (c, _) = splitTyConApp (typeOf (undefined :: TypVar V0)) in c
185+
186+skolem :: TyCon
187+skolem = let (c, _) = splitTyConApp (typeOf (undefined :: Skolem V0)) in c
188+
189+--------------------------------------------------------------------------------
190+-- Type variables --
191+--------------------------------------------------------------------------------
192+
193+data TypVar a deriving Typeable
194+data Skolem a deriving Typeable
195+data Zero deriving Typeable
196+data Succ a deriving Typeable
197+
198+type V0 = Zero
199+type V1 = Succ V0
200+type V2 = Succ V1
201+type V3 = Succ V2
202+type V4 = Succ V3
203+type V5 = Succ V4
204+type V6 = Succ V5
205+type V7 = Succ V6
206+type V8 = Succ V7
207+type V9 = Succ V8
208+
209+type ANY = TypVar V0
210+type ANY1 = TypVar V1
211+type ANY2 = TypVar V2
212+type ANY3 = TypVar V3
213+type ANY4 = TypVar V4
214+type ANY5 = TypVar V5
215+type ANY6 = TypVar V6
216+type ANY7 = TypVar V7
217+type ANY8 = TypVar V8
218+type ANY9 = TypVar V9
219+
220+--------------------------------------------------------------------------------
221+-- Operations on type reps --
222+--------------------------------------------------------------------------------
223+
224+-- | If 'isInstanceOf' fails it returns a type error
225+type TypeError = String
226+
227+-- | @t1 `isInstanceOf` t2@ checks if @t1@ is an instance of @t2@
228+isInstanceOf :: TypeRep -> TypeRep -> Either TypeError ()
229+isInstanceOf t1 t2 = void (unify (skolemize t1) t2)
230+
231+-- | @funResultTy t1 t2@ is the type of the result when applying a function
232+-- of type @t1@ to an argument of type @t2@
233+funResultTy :: TypeRep -> TypeRep -> Either TypeError TypeRep
234+funResultTy (splitTyConApp -> (fc, [farg, fres])) x | fc == funTc = do
235+ s <- unify (alphaRename "f" farg) (alphaRename "x" x)
236+ return (normalize (subst s (alphaRename "f" fres)))
237+funResultTy f _ =
238+ Left $ show f ++ " is not a function"
239+
240+--------------------------------------------------------------------------------
241+-- Alpha-renaming and normalization --
242+--------------------------------------------------------------------------------
243+
244+alphaRename :: String -> TypeRep -> TypeRep
245+alphaRename prefix (isTypVar -> Just x) =
246+ mkTypVar (mkTyConApp (mkTyCon prefix) [x])
247+alphaRename prefix (splitTyConApp -> (c, ts)) =
248+ mkTyConApp c (map (alphaRename prefix) ts)
249+
250+tvars :: TypeRep -> [Var]
251+tvars (isTypVar -> Just x) = [x]
252+tvars (splitTyConApp -> (_, ts)) = concatMap tvars ts
253+
254+normalize :: TypeRep -> TypeRep
255+normalize t = subst (zip (tvars t) anys) t
256+ where
257+ anys :: [TypeRep]
258+ anys = map mkTypVar (iterate succ zero)
259+
260+ succ :: TypeRep -> TypeRep
261+ succ = mkTyConApp succTyCon . (:[])
262+
263+ zero :: TypeRep
264+ zero = mkTyConApp zeroTyCon []
265+
266+mkTyCon :: String -> TyCon
267+mkTyCon = mkTyCon3 "rank1typeable" "Data.Rank1Typeable"
268+
269+succTyCon :: TyCon
270+succTyCon = let (c, _) = splitTyConApp (typeOf (undefined :: Succ Zero)) in c
271+
272+zeroTyCon :: TyCon
273+zeroTyCon = let (c, _) = splitTyConApp (typeOf (undefined :: Zero)) in c
274+
275+--------------------------------------------------------------------------------
276+-- Unification --
277+--------------------------------------------------------------------------------
278+
279+type Substitution = [(Var, TypeRep)]
280+type Equation = (TypeRep, TypeRep)
281+type Var = TypeRep
282+
283+skolemize :: TypeRep -> TypeRep
284+skolemize (isTypVar -> Just x) = mkTyConApp skolem [x]
285+skolemize (splitTyConApp -> (c, ts)) = mkTyConApp c (map skolemize ts)
286+
287+occurs :: Var -> TypeRep -> Bool
288+occurs x (isTypVar -> Just x') = x == x'
289+occurs x (splitTyConApp -> (_, ts)) = any (occurs x) ts
290+
291+subst :: Substitution -> TypeRep -> TypeRep
292+subst s (isTypVar -> Just x) = fromMaybe (mkTypVar x) (lookup x s)
293+subst s (splitTyConApp -> (c, ts)) = mkTyConApp c (map (subst s) ts)
294+
295+unify :: TypeRep
296+ -> TypeRep
297+ -> Either TypeError Substitution
298+unify = \t1 t2 -> go [] [(t1, t2)]
299+ where
300+ go :: Substitution
301+ -> [Equation]
302+ -> Either TypeError Substitution
303+ go acc [] =
304+ return acc
305+ go acc ((t1, t2) : eqs) | t1 == t2 = -- Note: equality check is fast
306+ go acc eqs
307+ go acc ((isTypVar -> Just x, t) : eqs) =
308+ if x `occurs` t
309+ then Left "Occurs check"
310+ else go ((x, t) : map (second $ subst [(x, t)]) acc)
311+ (map (subst [(x, t)] *** subst [(x, t)]) eqs)
312+ go acc ((t, isTypVar -> Just x) : eqs) =
313+ go acc ((mkTypVar x, t) : eqs)
314+ go acc ((splitTyConApp -> (c1, ts1), splitTyConApp -> (c2, ts2)) : eqs) =
315+ if c1 /= c2
316+ then Left $ "Cannot unify " ++ show c1 ++ " and " ++ show c2
317+ else go acc (zip ts1 ts2 ++ eqs)
318+
319+--------------------------------------------------------------------------------
320+-- Pretty-printing --
321+--------------------------------------------------------------------------------
322+
323+instance Show TypeRep where
324+ showsPrec p (splitTyConApp -> (tycon, tys)) =
325+ case tys of
326+ [] -> showsPrec p tycon
327+ [anyIdx -> Just i] | tycon == typVar -> showString "ANY" . showIdx i
328+ [x] | tycon == listTc ->
329+ showChar '[' . shows x . showChar ']'
330+ [a,r] | tycon == funTc ->
331+ showParen (p > 8) $ showsPrec 9 a
332+ . showString " -> "
333+ . showsPrec 8 r
334+ xs | isTupleTyCon tycon ->
335+ showTuple xs
336+ _ ->
337+ showParen (p > 9) $ showsPrec p tycon
338+ . showChar ' '
339+ . showArgs tys
340+ where
341+ showIdx 0 = showString ""
342+ showIdx i = shows i
343+
344+showArgs :: Show a => [a] -> ShowS
345+showArgs [] = id
346+showArgs [a] = showsPrec 10 a
347+showArgs (a:as) = showsPrec 10 a . showString " " . showArgs as
348+
349+anyIdx :: TypeRep -> Maybe Int
350+anyIdx (splitTyConApp -> (c, [])) | c == zeroTyCon = Just 0
351+anyIdx (splitTyConApp -> (c, [t])) | c == succTyCon = (+1) <$> anyIdx t
352+anyIdx _ = Nothing
353+
354+showTuple :: [TypeRep] -> ShowS
355+showTuple args = showChar '('
356+ . foldr (.) id ( intersperse (showChar ',')
357+ $ map (showsPrec 10) args
358+ )
359+ . showChar ')'
360+
361+isTupleTyCon :: TyCon -> Bool
362+isTupleTyCon = isPrefixOf "(," . tyConName