A state machine is model that describes the behavior of something, for example lightbulb. Finite state machines describe how state of an object transitions to another state when an events occurs.
Info
State machines help build reliable and robust software. Read more about the benefits of state machines.
Creating a state machine
In XFsm, a state machine is created using the XFsm.Machine module:
defmodule Feedback do
use XFsm.Actor
use XFsm.Machine
initial :question
state :question do
on :"feedback.good", do: target :thanks
end
state :thanks do
# ...
end
# ...
endIn this example, the machine has two states: question and thanks. The question state has a transition to the thanks state when the feedback.good event is sent to the machine.
{:ok, pid} = Feedback.start_link()
XFsm.Actor.subscribe(pid, &IO.inspect(&1.state))
# logs 'question'
XFsm.Actor.send(pid, %{type: :"feedback.good"})
# logs 'thanks'Creating GenServers from machines
A machine contains the logic of a GenServer. Multiple GenServers can be created from the same machine declaration, and each of those processes will exhibit the same behavior, reaction to received events.
To create a GenServer make sure your machine has the line use XFsm.Actor or call XFsm.Actor.start_link/1.
{:ok, pid} = Feedback.start_link()
# OR
{:ok, pid} = XFsm.Actor.start_link(module: Feedback)
XFsm.Actor.subscribe(pid, &IO.inspect(&1.state))
# logs 'question'Providing implementations
Machine implementations are language-specific code that is executed but not directly related to the state machine's logic (states and transitions). This includes:
- Actions, which are fire-and-forget side-effects.
- Guards, which are conditions that determines whether a transition should be taken.
You can override the default implementations by providing implementations via options to the machine.
defmodule Feedback do
use XFsm.Actor
use XFsm.Machine
initial :rate
state :rate do
entry :do_something
end
def do_something(_), do: IO.puts("hello")
end
{:ok, pid} = Feedback.start_link(actions: %{do_something: fn _ -> IO.puts("world") end})
# logs 'world'