SciFlow-0.7.0: src/Control/Workflow/Interpreter/FunctionTable.hs
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE LambdaCase #-}
-- This interpreter treat a workflow as a function lookup table.
-- Given a (key, state, input) tuple where state and input are in binary format
-- , the table will return the output in binary format.
module Control.Workflow.Interpreter.FunctionTable
( mkDict
, mkFunTable
) where
import Data.Binary
import Language.Haskell.TH
import qualified Data.ByteString.Lazy as B
import qualified Data.Text as T
import Control.Arrow.Free (Free, eval)
import Control.Arrow
import Control.Distributed.Process.Closure (functionTDict, mkClosure, remotableDecl)
import Control.Distributed.Process.Node (initRemoteTable)
import Control.Monad.Reader
import Control.Monad.Catch (SomeException(..), catch)
import qualified Control.Category as C
import Control.Distributed.Process (Process)
import Control.Concurrent.MVar
import Control.Workflow.Types
mkFunTable :: String -- ^ Name of the table
-> String -- ^ SciFlow env i o
-> Q [Dec]
mkFunTable nm wf = remotableDecl [sig, fun, dec]
where
funName = mkName $ nm ++ "__dict"
sig = fmap return $ funName `sigD`
[t| (T.Text, B.ByteString, B.ByteString) -> Process (Either String B.ByteString) |]
fun = [d| $(varP funName) = liftIO . mkDict $(varE $ mkName wf) |]
dec = [d| $(varP $ mkName nm) = FunctionTable $(mkClosure funName) $(functionTDict funName) $ __remoteTableDecl initRemoteTable |]
-- | Function table
type Dictionary = (T.Text, B.ByteString, B.ByteString) -- ^ Input
-> IO (Either String B.ByteString) -- ^ Output
mkDict :: Binary env
=> Free (Flow env) i o
-> Dictionary
mkDict flow (nm, env, input) = do
res <- newMVar $ Left ""
unA $ eval (go res) flow
readMVar res
where
go res (Step job) = A $ modifyMVar_ res $ \case
Left msg -> if nm == _job_name job
then catch (runJob job) $ \(SomeException e) -> return $ Left $ show e
else return $ Left msg
x -> return x
where
runJob job = Right . encode <$>
runReaderT (f (decode input)) (decode env)
where
f = runKleisli $ eval (Kleisli . _unAction) $ _job_action job
-- | Helper type
data A a b = A { unA :: IO () }
instance C.Category A where
id = A $ return ()
A f . A g = A (g >> f)
instance Arrow A where
arr _ = A $ return ()
A f *** A g = A (f >> g)