----------------------------------------------------------------------------- -- | -- Module : Control.Behaviour.GenStatem -- Copyright : (c) 2020-2021 EMQ Technologies Co., Ltd. -- License : BSD-style (see the LICENSE file) -- -- Maintainer : Feng Lee, feng@emqx.io -- Yang M, yangm@emqx.io -- Stability : experimental -- Portability : portable -- -- The GenStatem Behaviour. -- ----------------------------------------------------------------------------- module Control.Behaviour.GenStatem ( class GenStatem , Init , OnEvent , HandleEvent , handleEvent , handleWith -- * start/stop , start , startWith , startLink , startLinkWith , startMonitor , startMonitorWith , stop , stopPid , stopRef , stopWith -- * client apis , call , callTo , callRef , callTimeout , cast , castTo , castRef , sendRequest -- * init result , initOk , initOkWith , initIgnore , initStop -- * transition , keep , keepWith , next , nextWith , repeat , repeatWith , shutdown -- * helper funcs , replyTo , unhandled , module Control.Behaviour.GenStatem.Types ) where import Control.Monad (pure) import Control.Process (Process, ExitReason(..)) import Data.Function (($), (<<<)) import Data.Map as M import Data.Pid (Pid) import Data.Ref (Reference) import Data.Timeout (Timeout) import Data.Unit (Unit) import Foreign (ffiIO1, ffiIO2) import Control.Behaviour.GenStatem.Types -- | Init Action type Init e s d = Process (InitResult e s d) -- | Handle Event type HandleEvent e s d = EventType -> e -> s -> d -> Process (Transition e s d) -- | On Event type OnEvent e s d = EventType -> e -> d -> Process (Transition e s d) class GenStatem e s d | e -> s, s -> d, d -> e where handleEvent :: HandleEvent e s d foreign import start :: forall e s d. GenStatem e s d => (Init e s d) -> Process Pid foreign import startWith :: forall e s d. GenStatem e s d => Name -> (Init e s d) -> Process Pid foreign import startLink :: forall e s d. GenStatem e s d => (Init e s d) -> Process Pid foreign import startLinkWith :: forall e s d. GenStatem e s d => Name -> (Init e s d) -> Process Pid foreign import startMonitor :: forall e s d. GenStatem e s d => (Init e s d) -> Process (Pid, Reference) foreign import startMonitorWith :: forall e s d. GenStatem e s d => Name -> (Init e s d) -> Process (Pid, Reference) -- Stop the Statem Process. stop :: Name -> Process () stop = ffiIO1 :gen_statem :stop stopPid :: Pid -> Process () stopPid = ffiIO1 :gen_statem :stop -- Stop the Statem Process. foreign import stopRef :: StatemRef -> Process () -- Stop the Statem Process with ExitReason. foreign import stopWith :: StatemRef -> ExitReason -> Timeout -> Process () call :: forall req rep. Name -> req -> Process rep call = ffiIO2 :gen_statem :call -- | Synchronous call to the Server process directly. callTo :: forall req rep. Pid -> req -> Process rep callTo = ffiIO2 :gen_statem :call foreign import callRef :: forall req rep. StatemRef -> req -> Process rep foreign import callTimeout :: forall req rep. StatemRef -> req -> Timeout -> Process rep cast :: forall msg. Name -> msg -> Process () cast = ffiIO2 :gen_statem :cast -- | Sends an asynchronous request to the Server process directly. castTo :: forall req. Pid -> req -> Process () castTo = ffiIO2 :gen_statem :cast foreign import castRef :: forall msg. StatemRef -> msg -> Process () replyTo :: forall rep. From -> rep -> Process () replyTo = ffiIO2 :gen_statem :reply foreign import sendRequest :: forall req. StatemRef -> req -> Process RequestId -- TODO: -- wait_response(RequestId :: request_id()) -> -- {reply, Reply :: term()} | {error, {term(), server_ref()}} -- | Handle with state functions. handleWith :: forall e s d. [(s, OnEvent e s d)] -> HandleEvent e s d handleWith stateFuns = let funsMap = M.fromList stateFuns in \t e s d -> let f = M.get s funsMap in f t e d foreign import unhandled :: forall e s d. HandleEvent e s d ----------------------------------------------------------------------------- -- | Init result ----------------------------------------------------------------------------- initOk :: forall e s d. s -> d -> Process (InitResult e s d) initOk s d = pure $ InitOk s d [] initOkWith :: forall e s d. s -> d -> [Action e] -> Process (InitResult e s d) initOkWith s d = pure <<< InitOk s d initIgnore :: forall e s d. Process (InitResult e s d) initIgnore = pure InitIgnore initStop :: forall e s d. ExitReason -> Process (InitResult e s d) initStop = pure <<< InitStop ----------------------------------------------------------------------------- -- | State Transition ----------------------------------------------------------------------------- keep :: forall e s d. d -> Process (Transition e s d) keep d = pure $ Keep d [] keepWith :: forall e s d. d -> [Action e] -> Process (Transition e s d) keepWith d = pure <<< Keep d next :: forall e s d. s -> d -> Process (Transition e s d) next s d = pure $ Next s d [] nextWith :: forall e s d. s -> d -> [Action e] -> Process (Transition e s d) nextWith s d = pure <<< Next s d repeat :: forall e s d. d -> Process (Transition e s d) repeat d = pure $ Repeat d [] repeatWith :: forall e s d. d -> [Action e] -> Process (Transition e s d) repeatWith d = pure <<< Repeat d shutdown :: forall e s d. Atom -> d -> Process (Transition e s d) shutdown r = pure <<< Shutdown (ExitReason r)