module StateMachine = type State<'Event> = | Next of ('Event -> State<'Event>) | Stop let feed state event = match state with | Stop -> failwith "Terminal state reached" | Next handler -> event |> handler type StateMachine<'event>(initial: State<'event>) = let mutable current = initial member this.Fire event = current <- feed current event member this.IsStopped with get () = match current with | Stop -> true | _ -> false let createMachine initial = StateMachine(initial) let createAgent initial = MailboxProcessor.Start (fun inbox -> let rec loop state = async { let! event = inbox.Receive () match event |> feed state with | Stop -> () | Next _ as next -> return! loop next } loop initial ) module DoorMachine = open StateMachine type Event = | Open | Close | Lock | Unlock let configureDoor sound = let rec opened event = match event with | Close -> sound "bang"; Next (closed false) | Lock -> sound "clack"; Next opened | _ -> Next opened and closed locked event = match event with | Open when locked -> sound "dumdum"; Next (closed locked) | Open -> sound "squeak"; Next opened | Lock -> sound "click"; Next (closed true) | Unlock -> sound "clack"; Next (closed false) | _ -> Next (closed locked) Next (closed false) let test sound = let agent = sound |> configureDoor |> StateMachine.createAgent agent.Post Lock agent.Post Unlock agent.Post Open agent.Post Close [] let main argv = DoorMachine.test (printfn "%s") System.Console.ReadLine() |> ignore 0 // return an integer exit code