This Haskell library is intended to ease usage of monad transformer stacks with the Cloud Haskell distributed-process
library. This package provides typeclasses and functions for lifting functions and control operations (such as spawnLocal
) from the Process
monad
into transformer stacks based on the Process
monad, in very similar way that the lifted-base
package lifts IO functions into more generalized functions constrained to MonadIO and MonadBaseControl IO.
This library uses MonadTransControl and a new typeclass - Control.Distributed.Process.Lifted.Class.MonadProcessBase
- which plays the same role as MonadBaseControl IO. Instances are provided for all the transformers types - so stacks based on any of these (e.g. ReaderT Config Process a
) can be used out-of-the-box.
The Control.Distributed.Process.Lifted
module exports all the same symbols as found in Control.Distributed.Process, but they are all generalized. Where appropriate it re-exports the more general functions from lifted-base (e.g. catch) rather than the versions re-implemented for Process
.
The Control.Distributed.Process.Lifted.Extras
module provides some useful utilities for working with Cloud Haskell on top of another IO-based monad.
For a motivation at a glance, consider the following two programs which perform the same function using the same monad stack, the first using lifted functions and the second using the base functions.
import Network.Transport.TCP (createTransport, defaultTCPParameters)
import Control.Distributed.Process.Lifted
import Control.Distributed.Process.Node
import Control.Concurrent.Lifted (threadDelay)
import Control.Monad.Reader (ReaderT, runReaderT, ask)
type MyConfig = String
type MyApp a = ReaderT MyConfig Process a
runMyApp :: LocalNode -> MyConfig -> MyApp () -> IO ()
runMyApp node conf ma = runProcess node (runReaderT ma conf)
useSomeConfig :: MyApp ()
useSomeConfig = ask >>= say
main :: IO ()
main = do
Right t <- createTransport "127.0.0.1" "10501" defaultTCPParameters
node <- newLocalNode t initRemoteTable
runMyApp node "some config data" $ do
parent <- getSelfPid
spawnLocal $ do
useSomeConfig
send parent "all done"
"all done" <- expect
return ()
threadDelay (50*1000)
And using the Base Process:
import Network.Transport.TCP (createTransport, defaultTCPParameters)
import Control.Distributed.Process
import Control.Distributed.Process.Node
import Control.Concurrent.Lifted (threadDelay)
import Control.Monad.Reader (ReaderT, runReaderT, ask, lift)
type MyConfig = String
type MyApp a = ReaderT MyConfig Process a
runMyApp :: LocalNode -> MyConfig -> MyApp () -> IO ()
runMyApp node conf ma = runProcess node (runReaderT ma conf)
withMyApp :: MyConfig -> MyApp a -> Process a
withMyApp conf ma = runReaderT ma conf
useSomeConfig :: MyApp ()
useSomeConfig = do
config <- ask
lift $ say config
main :: IO ()
main = do
Right t <- createTransport "127.0.0.1" "10501" defaultTCPParameters
node <- newLocalNode t initRemoteTable
runMyApp node "some config data" $ do
parent <- lift getSelfPid
config <- ask
lift $ spawnLocal $ do
withMyApp config $ do
useSomeConfig
lift $ send parent "all done"
"all done" <- lift expect
return ()
threadDelay (50*1000)