GenStatem
Event-Driven FSM
See: Erlang gen_statem Behaviour
State(S) x Event(E) -> Actions(A), State(S')
GenStatem Typeclass
class GenStatem e s d | e -> s, s -> d, d -> e where
handleEvent :: HandleEvent e s d
CodeLock Example
module Demo.FSM.CodeLock
( name
, start
, push
, stop
) where
import Prelude
import Control.Behaviour.GenStatem
( class GenStatem
, Action(..)
, EventType(..)
, Init
, OnEvent
, initOk
, handleWith
, unhandled
)
import Control.Behaviour.GenStatem as FSM
data Event = Button Integer | Lock
data State = Locked | Opened
data Data = Data
{ code :: [Integer]
, length :: Integer
, buttons :: [Integer]
}
instance Eq State where
eq Locked Locked = true
eq Opened Opened = true
eq _ _ = false
instance GenStatem Event State Data where
handleEvent = handleWith [(Locked, locked), (Opened, opened)]
name :: Atom
name = :code_lock
start :: [Integer] -> Process Pid
start code = FSM.startLinkWith name (init code)
push :: Integer -> Process ()
push n = FSM.cast name (Button n)
stop :: Process ()
stop = FSM.stop name
init :: [Integer] -> Init Event State Data
init code = initOk Locked d
where d = Data $ { code = reverse code
, length = length code
, buttons = []
}
locked :: OnEvent Event State Data
locked Cast (Button n) (Data d) =
let buttons = take d.length [n|d.buttons]
in if buttons == d.code then
let actions = [StateTimeout 1000 Lock] in
FSM.nextWith Opened (Data d{buttons = []}) actions
else FSM.keep (Data d{buttons = buttons})
locked t e d = unhandled t e Locked d
opened :: OnEvent Event State Data
opened Cast (Button _) d = FSM.keep d
opened Timeout Lock d = do
println "Timeout Lock"
FSM.next Locked d
opened t e d = unhandled t e Opened d
Start a FSM process
-- | Start a standalone FSM process
start :: forall e s d. GenStatem e s d => (Init e s d) -> Process Pid
startWith :: forall e s d. GenStatem e s d => Name -> (Init e s d) -> Process Pid
-- | Start a FSM process as part of a supervision tree.
startLink :: forall e s d. GenStatem e s d => (Init e s d) -> Process Pid
startLinkWith :: forall e s d. GenStatem e s d => Name -> (Init e s d) -> Process Pid
Init callback
-- | Init Result
data InitResult e s d
= InitOk s d [Action e]
-- ^ {ok, State, Actions}
| InitIgnore
-- ^ ignore
| InitStop ExitReason
-- ^ {stop, Reason}
-- | Init Action
type Init e s d = Process (InitResult e s d)
HandleEvent callback
-- | Event Type
data EventType
= Call From | Cast | Info
-- ^ external event type
| Timeout
-- ^ timeout event type
| Internal
-- ^ internal
-- | Statem Transition
data Transition e s d
= Keep d [Action e]
| Next s d [Action e]
| Repeat d [Action e]
| Shutdown ExitReason d
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)
-- | Handle with state functions.
handleWith :: forall e s d. [(s, OnEvent e s d)] -> HandleEvent e s d
Client APIs
call :: forall req rep. Name -> req -> Process rep
cast :: forall msg. Name -> msg -> Process ()
当前内容版权归 hamler-lang 或其关联方所有,如需对内容或内容相关联开源项目进行关注与资助,请访问 hamler-lang .