@@ -1,29 +1,26 @@
1−-- | Implementation of 'Closure' that works around the absence of 'static'.
1+-- | Static values and Closures
22--
3−-- [User-defined monomorphic closures]
3+-- [Static values]
44--
5−-- Suppose we have a monomorphic function
5+-- /Towards Haskell in the Cloud/ (Epstein et al., Haskell Symposium 2011)
6+-- proposes a new type construct called 'static' that characterizes values that
7+-- are known statically. There is no support for 'static' in ghc yet, however,
8+-- so we emulate it using Template Haskell. Given a top-level definition
69--
7−-- > addInt :: Int -> Int -> Int
8−-- > addInt x y = x + y
10+-- > f :: forall a1 .. an. T
11+-- > f = ...
912--
10−-- Then the Template Haskell splice
11−--
12−-- > remotable ['addInt]
13+-- you can use a Template Haskell splice to create a static version of 'f':
1314--
14−-- creates a function
15−--
16−-- > $(mkClosure 'addInt) :: Int -> Closure (Int -> Int)
15+-- > $(mkStatic 'f) :: forall a1 .. an. Static T
1716--
18−-- which can be used to partially apply 'addInt' and turn it into a 'Closure',
19−-- which can be sent across the network. Closures can be deserialized with
20−--
21−-- > unClosure :: Typeable a => Closure a -> Process a
17+-- Every module that you write that contains calls to 'mkStatic' needs to
18+-- have a call to 'remotable':
2219--
23−-- In general, given a monomorphic function @f :: T1 -> T2@ the corresponding
24−-- function @$(mkClosure 'f)@ will have type @T1 -> Closure T2@.
20+-- > remotable [ 'f, 'g, ... ]
2521--
26−-- The call to 'remotable' will also generate a function
22+-- where you must pass every function (or other value) that you pass as an
23+-- argument to 'mkStatic'. The call to 'remotable' will create a definition
2724--
2825-- > __remoteTable :: RemoteTable -> RemoteTable
2926--
@@ -31,62 +28,124 @@
3128-- Cloud Haskell. You should have (at most) one call to 'remotable' per module,
3229-- and compose all created functions when initializing Cloud Haskell:
3330--
34−-- > let rtable = M1.__remoteTable
31+-- > let rtable :: RemoteTable
32+-- > rtable = M1.__remoteTable
3533-- > . M2.__remoteTable
3634-- > . ...
3735-- > . Mn.__remoteTable
3836-- > $ initRemoteTable
3937--
40−-- See Section 6, /Faking It/, of /Towards Haskell in the Cloud/ for more info.
38+-- [Composing static values]
39+--
40+-- We generalize the notion of 'static' as described in the paper, and also
41+-- provide
42+--
43+-- > staticApply :: Static (a -> b) -> Static a -> Static b
44+--
45+-- This makes it possible to define a rich set of combinators on 'static'
46+-- values, a number of which are provided in this module.
47+--
48+-- [Closures]
49+--
50+-- Suppose you have a process
51+--
52+-- > factorial :: Int -> Process Int
4153--
42−-- [User-defined polymorphic closures]
54+-- Then you can use the supplied Template Haskell function 'mkClosure' to define
4355--
44−-- Suppose we have a polymorphic function
56+-- > factorialClosure :: Int -> Closure (Process Int)
57+-- > factorialClosure = $(mkClosure 'factorial)
4558--
46−-- > first :: forall a b. (a, b) -> a
47−-- > first (x, _) = x
59+-- You can then pass 'factorialClosure n' to 'spawn', for example, to have a
60+-- remote node compute a factorial number.
4861--
49−-- Then the Template Haskell splice
62+-- In general, if you have a /monomorphic/ function
5063--
51−-- > remotable ['first]
64+-- > f :: T1 -> T2
5265--
53−-- creates a function
66+-- then
5467--
55−-- > $(mkClosure 'first) :: forall a b. (Typeable a, Typeable b)
56−-- > => Closure (a -> b -> a)
68+-- > $(mkClosure 'f) :: T1 -> Closure T2
5769--
58−-- In general, given a polymorphic function @f :: forall a1 .. an. T@ the
59−-- corresponding function @$(mkClosure 'f)@ will have type
60−-- @forall a1 .. an. (Typeable a1, .., Typeable an) => Closure T@.
70+-- provided that 'T1' is serializable (*).
6171--
62−-- [Built-in closures]
72+-- [Creating closures manually]
6373--
64−-- We offer a number of standard commonly useful closures.
74+-- You don't /need/ to use 'mkClosure', however. Closures are defined exactly
75+-- as described in /Towards Haskell in the Cloud/:
76+--
77+-- > data Closure a = Closure (Static (ByteString -> a)) ByteString
78+--
79+-- The splice @$(mkClosure 'factorial)@ above expands to (prettified a bit):
80+--
81+-- > factorialClosure :: Int -> Closure (Process Int)
82+-- > factorialClosure n = Closure decoder (encode n)
83+-- > where
84+-- > decoder :: Static (ByteString -> Process Int)
85+-- > decoder = $(mkStatic 'factorial)
86+-- > `staticCompose`
87+-- > staticDecode $(functionSDict 'factorial)
6588--
66−-- [Closure combinators]
89+-- 'mkStatic' we have already seen:
6790--
68−-- Closures combinators allow to create closures from other closures. For
69−-- example, 'spawnSupervised' is defined as follows:
91+-- > $(mkStatic 'factorial) :: Static (Int -> Process Int)
7092--
71−-- > spawnSupervised :: NodeId
72−-- > -> Closure (Process ())
73−-- > -> Process (ProcessId, MonitorRef)
74−-- > spawnSupervised nid proc = do
75−-- > us <- getSelfPid
76−-- > them <- spawn nid (linkClosure us `cpSeq` proc)
77−-- > ref <- monitor them
78−-- > return (them, ref)
93+-- 'staticCompose' is function composition on static functions. 'staticDecode'
94+-- has type (**)
7995--
80−-- [Serializable Dictionaries]
96+-- > staticDecode :: Typeable a
97+-- > => Static (SerializableDict a) -> Static (ByteString -> a)
98+--
99+-- and gives you a static decoder, given a static Serializable dictionary.
100+-- 'SerializableDict' is a reified type class dictionary, and defined simply as
101+--
102+-- > data SerializableDict a where
103+-- > SerializableDict :: Serializable a => SerializableDict a
81104--
82−-- Some functions (such as 'sendClosure' or 'returnClosure') require an
83−-- explicit (reified) serializable dictionary. To create such a dictionary do
105+-- That means that for any serialziable type 'T', you can define
84106--
85−-- > sdictInt :: SerializableDict Int
86−-- > sdictInt = SerializableDict
107+-- > sdictForMyType :: SerializableDict T
108+-- > sdictForMyType = SerializableDict
109+--
110+-- and then use
111+--
112+-- > $(mkStatic 'sdictForMyType) :: Static (SerializableDict T)
113+--
114+-- to obtain a static serializable dictionary for 'T' (make sure to pass
115+-- 'sdictForMyType' to 'remotable').
116+--
117+-- However, since these serialization dictionaries are so frequently required,
118+-- when you call 'remotable' on a monomorphic function @f : T1 -> T2@
87119--
88−-- and then pass @'sdictInt@ to 'remotable'. This will fail if the
89−-- type is not serializable.
120+-- > remotable ['f]
121+--
122+-- then a serialization dictionary is automatically created for you, which you
123+-- can access with
124+--
125+-- > $(functionDict 'f) :: Static (SerializableDict T1)
126+--
127+-- This is the dictionary that 'mkClosure' uses.
128+--
129+-- [Combinators on Closures]
130+--
131+-- Support for 'staticApply' (described above) also means that we can define
132+-- combinators on Closures, and we provide a number of them in this module,
133+-- the most important of which is 'cpBind'. Have a look at the implementation
134+-- of 'Control.Distributed.Process.call' for an example use.
135+--
136+-- [Notes]
137+--
138+-- (*) If 'T1' is not serializable you will get a type error in the generated
139+-- code. Unfortunately, the Template Haskell infrastructure cannot check
140+-- a priori if 'T1' is serializable or not due to a bug in the Template
141+-- Haskell libraries (<http://hackage.haskell.org/trac/ghc/ticket/7066>)
142+--
143+-- (**) Even though 'staticDecode' is passed an explicit serialization
144+-- dictionary, we still need the 'Typeable' constraint because
145+-- 'Static' is not the /true/ static. If it was, we could 'unstatic'
146+-- the dictionary and pattern match on it to bring the 'Typeable'
147+-- instance into scope, but unless proper 'static' support is added to
148+-- ghc we need both the type class argument and the explicit dictionary.
90149module Control.Distributed.Process.Closure
91150 ( -- * User-defined closures
92151 remotable
@@ -98,6 +157,9 @@ module Control.Distributed.Process.Closure
98157 , staticDuplicate
99158 -- * Static functionals
100159 , staticConst
160+ , staticFlip
161+ , staticFst
162+ , staticSnd
101163 , staticCompose
102164 , staticFirst
103165 , staticSecond
@@ -108,6 +170,22 @@ module Control.Distributed.Process.Closure
108170 , staticDecode
109171 , staticClosure
110172 , toClosure
173+ -- * Serialization dictionaries (and their static versions)
174+ , SerializableDict(..)
175+ , sdictUnit
176+ , sdictProcessId
177+ , sdictSendPort
178+ -- * Definition of CP and the generalized arrow combinators
179+ , CP
180+ , cpIntro
181+ , cpElim
182+ , cpId
183+ , cpComp
184+ , cpFirst
185+ , cpSecond
186+ , cpSplit
187+ , cpCancelL
188+ , cpCancelR
111189 -- * Closure versions of CH primitives
112190 , cpLink
113191 , cpUnlink
@@ -118,11 +196,6 @@ module Control.Distributed.Process.Closure
118196 , cpReturn
119197 , cpBind
120198 , cpSeq
121− -- * Serialization dictionaries
122− , SerializableDict(..)
123− , sdictUnit
124− , sdictProcessId
125− , sdictSendPort
126199 ) where
127200
128201import Control.Distributed.Process.Internal.Types
@@ -138,11 +211,14 @@ import Control.Distributed.Process.Internal.Closure.TH
138211import Control.Distributed.Process.Internal.Closure.Static
139212 ( -- Static functionals
140213 staticConst
214+ , staticFlip
215+ , staticFst
216+ , staticSnd
141217 , staticCompose
142218 , staticFirst
143219 , staticSecond
144220 , staticSplit
145− -- Constants
221+ -- Static constants
146222 , staticUnit
147223 -- Creating closures
148224 , staticDecode
@@ -155,8 +231,19 @@ import Control.Distributed.Process.Internal.Closure.Static
155231 )
156232import Control.Distributed.Process.Internal.Closure.MkClosure (mkClosure)
157233import Control.Distributed.Process.Internal.Closure.CP
158− ( -- Closure versions of CH primitives
159− cpLink
234+ ( -- Definition of CP and the generalized arrow combinators
235+ CP
236+ , cpIntro
237+ , cpElim
238+ , cpId
239+ , cpComp
240+ , cpFirst
241+ , cpSecond
242+ , cpSplit
243+ , cpCancelL
244+ , cpCancelR
245+ -- Closure versions of CH primitives
246+ , cpLink
160247 , cpUnlink
161248 , cpSend
162249 , cpExpect