State Machines in Elixir with gen_statem

by Jake Prem


A note on Erlang

  • VariablesAreUppercase
  • atoms_are_lowercase

What is it?

What is a State Machine?


  • Added in Erlang/OTP 19.0 (2016)
  • Replaces the deprecated gen_fsm

Title Text

GenServer vs gen_statem

  • GenServer for simple cases
  • gen_statem for more complex cases
    • Built in timeout functionality

What is a GenServer?

Callback Modes

  • State Functions
    • State must be an atom
  • Handle Event Function
    • State can be any valid Erlang term
  • State Enter
    • A special case that will trigger an enter
      event on state changes
def callback_mode do
  [:handle_event_function, :state_enter]
  [:state_functions, :state_enter]

Return values

{:next_state, NextState, NewData, Actions[]}
{:next_state, NextState, NewData}

{:keep_state, NewData, Actions[]}
{:keep_state, NewData}

{:keep_state_and_data, Actions[]}

{:repeat_state, NewData, Actions[]}
{:repeat_state, NewData}

{:repeat_state_and_data, Actions[]}

{:stop, Reason, NewData}
{:stop, Reason}
{:stop_and_reply, Reason, NewData, ReplyActions[]}
{:stop_and_reply, Reason, ReplyActions[]}

Return Actions

{:postpone, Boolean}

{:hibernate, Boolean}

# Cancelled on state_change, or {:state_timeout, :infinity}
{:state_timeout, Time, EventContent}

{{:timeout, Name}, Time}

{:timeout, Time}

# --------------------------------
{:reply, From, Reply}

{:next_event, EventType, EventContent}

Server vs Statem

# GenServer
def handle_call(msg, from, state) do
{:reply, reply_msg, state}

def handle_cast(msg, state) do
{:noreply, state}

# GenStatem
def handle_event({:call, from}, msg_content, state, data) do
{..., [{:reply, from, reply_msg} | other_actions]}

def handle_cast(:cast, msg_content, state, data) do
{..., actions}

A Note on names

# GenServer
GenServer.start_link(__MODULE__, args, name: :whatever)

# :gen_statem
:gen_statem.start_link({:local, :whatever}, __MODULE__, args, opts)

How do we use it?