corecursive-main-0.1.0.0: src/System/Process/Corecursive/Base.hs
-- | Types for building (co-)recursive system programs.
--
-- The tools in this module allow to define the call-site and the main entry
-- point of a (co-)recursive program.
--
-- However, this module does not define 'callback' usages so that implementers
-- are free to chose the mechanism they want to:
-- - start the recursive program
-- - retrieve a return-value (if the recursive program ever returns)
module System.Process.Corecursive.Base
( App (..)
, app
, runApp
, Self (..)
) where
-- | A datatype representing an instance of the (co-)recursive program.
--
-- A reason why the records in this type are separate from 'App' is that a
-- calling program may need to execute a fair amount of progress
-- before knowing the right 'executable' (e.g., in the case of a remote
-- invocation). Also, sometimes one may want to adapt 'Self'.
data Self t msg arg inst = Self {
executable :: t inst
-- ^ An action to retrieve an instance of the (co-)recursive program
-- the 'inst' type is leaved as an argument to allow representing cases where
-- the execution is actually remote.
, unparse :: arg -> t msg
-- ^ An action to unparse arguments. Typically from 'unparseArgs'.
}
instance Functor t => Functor (Self t msg arg) where
fmap f (Self e u) = Self (fmap f e) u
-- | A datatype wrapping everything needed to make a (co-)recursive main
-- function.
--
-- Application authors may find this type useful because it has a Functor
-- instance, allowing to adapt the result of a computation.
--
-- See 'app'.
data App t msg arg inst ret = App {
parseArgs :: msg -> t arg
-- ^ Function parsing argument from a serialized message.
, unparseArgs :: arg -> t msg
-- ^ Function un-parsing an argument into a serialized message.
, corecursiveProgram :: Self t msg arg inst -> arg -> t ret
-- ^ Actual program to run from a specification.
}
instance Functor t => Functor (App t msg arg inst) where
fmap f (App p u cp) =
App p u $ \self arg -> fmap f (cp self arg)
-- | Constructor for an App.
app
:: (msg -> m arg)
-- ^ Function parsing argument from a serialized message.
-> (arg -> m msg)
-- ^ Function un-parsing an argument into a serialized message.
-> (Self m msg arg inst -> arg -> m ret)
-- ^ Actual program to run from a specification.
-> App m msg arg inst ret
app = App
-- | Typical function to run an 'App' with simplifying assumptions:
--
-- - locate the Self instance once for the rest of the program
-- - reads the arguments once
-- - starts the actual program
runApp
:: Monad m
=> m inst
-- ^ An action to locate the self instance of an applictaion.
-- This action is run exactly once and the result is stored in 'Self'.
-> m msg
-- ^ An action to retrieve the message to start the application with.
-- This action is run exactly once, the result is then parsed and discarded.
-> App m msg arg inst ret
-- ^ The 'App' to run
-> m ret
runApp getInst getMsg (App parse unparse go) = do
inst <- getInst
let self = Self (pure inst) unparse
arg <- parse =<< getMsg
go self arg