-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Compositional, type-safe, polymorphic static values and closures -- -- Towards Haskell in the Cloud (Epstein et al, Haskell Symposium -- 2011) introduces the concept of static values: values that are -- known at compile time. In a distributed setting where all nodes are -- running the same executable, static values can be serialized simply by -- transmitting a code pointer to the value. This however requires -- special compiler support, which is not yet available in ghc. We can -- mimick the behaviour by keeping an explicit mapping -- (RemoteTable) from labels to values (and making sure that all -- distributed nodes are using the same RemoteTable). In this -- module we implement this mimickry and various extensions: type safety -- (including for polymorphic static values) and compositionality. @package distributed-static @version 0.2.1.1 -- | Towards Haskell in the Cloud (Epstein et al, Haskell Symposium -- 2011) introduces the concept of static values: values that are -- known at compile time. In a distributed setting where all nodes are -- running the same executable, static values can be serialized simply by -- transmitting a code pointer to the value. This however requires -- special compiler support, which is not yet available in ghc. We can -- mimick the behaviour by keeping an explicit mapping -- (RemoteTable) from labels to values (and making sure that all -- distributed nodes are using the same RemoteTable). In this -- module we implement this mimickry and various extensions. -- --
-- instance Binary (Static a) ---- -- This however is not (runtime) type safe: for instance, what would be -- the behaviour of -- --
-- f :: Static Int -> Static Bool -- f = decode . encode ---- -- For this reason we work only with Typeable terms in this -- module, and implement runtime checks -- --
-- instance Typeable a => Binary (Static a) ---- -- The above function f typechecks but throws an exception if -- executed. The type representation we use, however, is not the standard -- TypeRep from Data.Typeable but TypeRep from -- Data.Rank1Typeable. This means that we can represent -- polymorphic static values (see below for an example). -- -- Since the runtime mapping (RemoteTable) contains values of -- different types, it maps labels (Strings) to Dynamic -- values. Again, we use the implementation from Data.Rank1Dynamic -- so that we can store polymorphic dynamic values. -- --
-- staticLabel :: String -> Static a -- staticApply :: Static (a -> b) -> Static a -> Static b ---- -- The first constructor refers to a label in a RemoteTable. The -- second allows to apply a static function to a static argument, and -- makes Static compositional: once we have staticApply we -- can implement numerous derived combinators on Static values (we -- define a few in this module; see staticCompose, -- staticSplit, and staticConst). -- --
-- data Closure a = Closure (Static (ByteString -> a)) ByteString ---- -- See Towards Haskell in the Cloud for the rationale behind -- representing the function closure environment in serialized -- (ByteString) form. Any static value can trivially be turned -- into a Closure (staticClosure). Moreover, since -- Static is now compositional, we can also define derived -- operators on Closure values (closureApplyStatic, -- closureApply, closureCompose, closureSplit). -- --
-- sendInt :: ProcessId -> Int -> Process () ---- -- We might want to define -- --
-- sendIntClosure :: ProcessId -> Closure (Int -> Process ()) ---- -- In order to do that, we need a static version of send, and a -- static decoder for ProcessId: -- --
-- sendIntStatic :: Static (ProcessId -> Int -> Process ()) -- sendIntStatic = staticLabel "$send" ---- --
-- decodeProcessIdStatic :: Static (ByteString -> Int) -- decodeProcessIdStatic = staticLabel "$decodeProcessId" ---- -- where of course we have to make sure to use an appropriate -- RemoteTable: -- --
-- rtable :: RemoteTable -- rtable = registerStatic "$send" (toDynamic sendInt) -- . registerStatic "$decodeProcessId" (toDynamic (decode :: ByteString -> Int)) -- $ initRemoteTable ---- -- We can now define sendIntClosure: -- --
-- sendIntClosure :: ProcessId -> Closure (Int -> Process ()) -- sendIntClosure pid = closure decoder (encode pid) -- where -- decoder :: Static (ByteString -> Int -> Process ()) -- decoder = sendIntStatic `staticCompose` decodeProcessIdStatic ---- --
-- sendIntResult :: ProcessId -> Closure (Process Int) -> Closure (Process ()) ---- -- which turns a process that computes an integer into a process that -- computes the integer and then sends it someplace else. -- -- We can define -- --
-- bindStatic :: (Typeable a, Typeable b) => Static (Process a -> (a -> Process b) -> Process b) -- bindStatic = staticLabel "$bind" ---- -- provided that we register this label: -- --
-- rtable :: RemoteTable -- rtable = ... -- . registerStatic "$bind" ((>>=) :: Process ANY1 -> (ANY1 -> Process ANY2) -> Process ANY2) -- $ initRemoteTable ---- -- (Note that we are using the special ANY1 and ANY2 types -- from Data.Rank1Typeable to represent this polymorphic value.) -- Once we have a static bind we can define -- --
-- sendIntResult :: ProcessId -> Closure (Process Int) -> Closure (Process ()) -- sendIntResult pid cl = bindStatic `closureApplyStatic` cl `closureApply` sendIntClosure pid ---- --
-- send :: Binary a => ProcessId -> a -> Process () ---- -- If we now want to define sendClosure, analogous to -- sendIntClosure above, we somehow need to include the -- Binary instance in the closure -- after all, we can ship this -- closure someplace else, where it needs to accept an a, -- then encode it, and send it off. In order to do this, we need -- to turn the Binary instance into an explicit dictionary: -- --
-- data BinaryDict a where -- BinaryDict :: Binary a => BinaryDict a -- -- sendDict :: BinaryDict a -> ProcessId -> a -> Process () -- sendDict BinaryDict = send ---- -- Now sendDict is a normal polymorphic value: -- --
-- sendDictStatic :: Static (BinaryDict a -> ProcessId -> a -> Process ()) -- sendDictStatic = staticLabel "$sendDict" -- -- rtable :: RemoteTable -- rtable = ... -- . registerStatic "$sendDict" (sendDict :: BinaryDict ANY -> ProcessId -> ANY -> Process ()) -- $ initRemoteTable ---- -- so that we can define -- --
-- sendClosure :: Static (BinaryDict a) -> Process a -> Closure (a -> Process ()) -- sendClosure dict pid = closure decoder (encode pid) -- where -- decoder :: Static (ByteString -> a -> Process ()) -- decoder = (sendDictStatic `staticApply` dict) `staticCompose` decodeProcessIdStatic ---- --
-- rtable :: RemoteTable -- rtable = registerStatic "$sdictSendPort" sdictSendPort -- $ initRemoteTable -- where -- sdictSendPort :: SerializableDict ANY -> SerializableDict (SendPort ANY) -- sdictSendPort SerializableDict = SerializableDict ---- -- This definition of sdictSendPort ignores its argument -- completely, and constructs a SerializableDict for the -- monomorphic type SendPort ANY, which isn't what you -- want. Instead, you should do -- --
-- rtable :: RemoteTable -- rtable = registerStatic "$sdictSendPort" (sdictSendPort :: SerializableDict ANY -> SerializableDict (SendPort ANY)) -- $ initRemoteTable -- where -- sdictSendPort :: forall a. SerializableDict a -> SerializableDict (SendPort a) -- sdictSendPort SerializableDict = SerializableDict --module Control.Distributed.Static -- | A static value. Static is opaque; see staticLabel and -- staticApply. data Static a -- | Create a primitive static value. -- -- It is the responsibility of the client code to make sure the -- corresponding entry in the RemoteTable has the appropriate -- type. staticLabel :: String -> Static a -- | Apply two static values staticApply :: Static (a -> b) -> Static a -> Static b -- | Static version of (.) staticCompose :: (Typeable a, Typeable b, Typeable c) => Static (b -> c) -> Static (a -> b) -> Static (a -> c) -- | Static version of (***) staticSplit :: (Typeable a, Typeable a', Typeable b, Typeable b') => Static (a -> b) -> Static (a' -> b') -> Static ((a, a') -> (b, b')) -- | Static version of const staticConst :: (Typeable a, Typeable b) => Static a -> Static (b -> a) -- | Static version of flip staticFlip :: (Typeable a, Typeable b, Typeable c) => Static (a -> b -> c) -> Static (b -> a -> c) -- | A closure is a static value and an encoded environment data Closure a closure :: Static (ByteString -> a) -> ByteString -> Closure a -- | Convert a static value into a closure. staticClosure :: Typeable a => Static a -> Closure a -- | Apply a static function to a closure closureApplyStatic :: (Typeable a, Typeable b) => Static (a -> b) -> Closure a -> Closure b -- | Closure application closureApply :: (Typeable a, Typeable b) => Closure (a -> b) -> Closure a -> Closure b -- | Closure composition closureCompose :: (Typeable a, Typeable b, Typeable c) => Closure (b -> c) -> Closure (a -> b) -> Closure (a -> c) -- | Closure version of (***) closureSplit :: (Typeable a, Typeable a', Typeable b, Typeable b') => Closure (a -> b) -> Closure (a' -> b') -> Closure ((a, a') -> (b, b')) -- | Runtime dictionary for unstatic lookups data RemoteTable -- | Initial remote table initRemoteTable :: RemoteTable -- | Register a static label registerStatic :: String -> Dynamic -> RemoteTable -> RemoteTable -- | Resolve a static value unstatic :: Typeable a => RemoteTable -> Static a -> Either String a -- | Resolve a closure unclosure :: Typeable a => RemoteTable -> Closure a -> Either String a instance Typeable StaticLabel instance Typeable1 Static instance Typeable1 Closure instance Show StaticLabel instance Show (Static a) instance Show (Closure a) instance Typeable a => Binary (Closure a) instance Typeable a => Binary (Static a)