FSM or Finite State Machine, is one of powerful tool that OTP give us to develop applications and solutions.
The finite state machines can be used to define elements to interacts with an application. Begins in a defined state and throught events, can change its state until its ending.
A finite state machine can be somthing simpler as an alarm clock (or cron system), or a bank transaction, or a phone call, …
In this post I develop an example to show you the simple to develop this problems using OTP.
The Finite State Machine Generator
In the Erlang OTP set of functions, has one specific that is named gen_fsm. This is the short name to generator: finite state machine. As his name say, is a generator. The generators, in Eralng/OTP are behaviours, base code used as framework to develop a specific behaviour.
A module in Erlang is as simpler as define the first line with the module name, specify the functions export and, after that define the functions. The smallest module that you can write could has three lines of code.
The generators are different, these need a specific line to indicate the behaviour to implement and the definition of all callbacks for the base code.
Itself, the gen_fsm is the base code that keeps running and, when an event arrive, this code generate a call to a specific function (a callback). By convention, the callbacks has a specific form, both in name and in params, and the returns is normalized as well.
To make things easier, we rely on this template, but suppressing comments to not take up much.
Elevator example
This examples is very simple. Supose that we have an elevator to three floors: ground, first and second. This elevator has only two buttons labeled as: up and down; stops in every floor each time. Therefore, depending on the floor that is, the buttons has valid functionality or not.

If we are inside of elevator, in the first floor, we can press up button or down button, the elevator will have an state to go… but if we are in the ground floor (B), by example, press the down button has no sense.
Developing the solution
The code, giving the gen_fsm template will be simple. We have three states and two events by state. We have six functions to develop:
-module(elevator). -behaviour(gen_fsm). -compile([export_all]). % to do: change by -export(...). -record(state, {}). start_link() -> gen_fsm:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> {ok, ground_floor, #state{}}. ground_floor(down, State) -> io:format("Beeep!, invalid option~n", []), {next_state, ground_floor, State}; ground_floor(up, State) -> io:format("Up to first floor~n", []), {next_state, first_floor, State}. first_floor(down, State) -> io:format("Down to ground floor~n", []), {next_state, ground_floor, State}; first_floor(up, State) -> io:format("Up to second floor~n", []), {next_state, second_floor, State}. second_floor(down, State) -> io:format("Down to first floor~n", []), {next_state, first floor, State}; second_floor(up, State) -> io:format("Beeep!, invalid option~n", []), {next_state, second_floor, State}. % add two functions to make calls easy % they are optional: up_button() -> gen_fsm:send_event(?MODULE, up). down_button() -> gen_fsm:send_event(?MODULE, down). |
If you see the figure, and the code convertion, you will see that there are a direct relation between the schema and the code. This make develop very easy a state system as: bank transaction, internet payment, bidding, a phone call, a contest, a videogame, etc.
How works
The function start_link, and which are defined below, they are not really of the system itself, but helpers to run the state machine and interact with it.
The state machine, at the begining run init function. This function give an state and let pass data to keep between calls or events to FSM.
Once are running, the base code call the appropiate function, named as current state name.
Running the code
Once the write the code, we can run the Erlang virtual machine and run without problems:
$ erl
[...]
1> c(elevator). % compile the code.
{ok,elevator}
2> elevator:start_link(). % launch the state machine.
{ok,<0.38.0>}
3> elevator:down_button().
Beeep!, invalid option
ok
4> elevator:up_button().
Up to first floor
ok
The state machine does the calls to the functions, according to the state it is and the event que was launch.
Adding time
A good thing about FSM, is that, not only has events by calls, but also by time, therefore, in the example, we can add that, the elevator does not keep in the same floor more than 5 seconds, going up and down each time, until press a button.
Here we need the memory to know, if we going from up, then goes to down, when arrive to first floor, and vice versa.
Adding memory is as simpler as modify the state record to indicate the address to go:
-record(state, {address}). init([]) -> {ok, ground_floor, #state{address=up}, 5000}. |
The time is measured in miliseconds, therefore this code does the elevator start and, if not press a button in 5 seconds, the elevator goes up alone. In this case we add a new event:

The new functions:
ground_floor(down, _State) -> io:format("Beeep!, incorrect option~n", []), {next_state, ground_floor, #state{address=up}, 5000}; ground_floor(up, _State) -> io:format("Up to first floor~n", []), {next_state, first_floor, #state{address=up}, 5000}; ground_floor(timeout, _State) -> io:format("Up automatic to first floor~n", []), {next_state, first_floor, #state{address=up}, 5000}. first_floor(down, State) -> io:format("Down to ground floor~n", []), {next_state, ground_floor, State, 5000}; first_floor(up, State) -> io:format("Up to second floor~n", []), {next_state, second_floor, State, 5000}; first_floor(timeout, State) -> NextState = case State#state.address of up -> io:format("Up automatic to second floor~n", []), second_floor; down -> io:format("Down automatic to ground floor~n", []), ground_floor end, {next_state, NextState, State, 5000}. second_floor(down, State) -> io:format("Down to first floor~n", []), {next_state, first_floor, State, 5000}; second_floor(up, State) -> io:format("Beeep!, incorrect option~n", []), {next_state, second_floor, State, 5000}; second_floor(timeout, _State) -> io:format("Down automatic to first floor~n", []), {next_state, first_floor, #state{address=down}, 5000}. |
With this, we have the three states, with the three events, ergo, the code for the nine possible functions that can be in runtime.
In case of have a lot of states, we could use the handle_event format, the state name will be the first param and could admin throught code. The events works so, therefore in this sense, can be unify or optimize.
Ending
The FSM systems for Erlang, give us a big power to do, in easy fashion, an element that, in case of will be asked or generating evetns from several concurrent places, its interface does its behaviour will be the appropiate in each moment.