Slide 1

Slide 1 text

No content

Slide 2

Slide 2 text

Andrea Leopardi @whatyouhide

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

No content

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

gen_statem

Slide 7

Slide 7 text

but why?!

Slide 8

Slide 8 text

FSM

Slide 9

Slide 9 text

FSM initial state states state transitions

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

chilling_and_looking_sad_for_no_reason

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

Finite?

Slide 15

Slide 15 text

Age of AI

Slide 16

Slide 16 text

gen_statem

Slide 17

Slide 17 text

No content

Slide 18

Slide 18 text

@behaviour :gen_statem

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

init/1 terminate/3 code_change/4 format_status/2

Slide 21

Slide 21 text

def callback_mode do :state_functions end

Slide 22

Slide 22 text

handle_call/3 handle_cast/2 handle_info/2

Slide 23

Slide 23 text

/3

Slide 24

Slide 24 text

( event_type, event_content, data )

Slide 25

Slide 25 text

( event_type, event_content, data )

Slide 26

Slide 26 text

( event_type, event_content, data )

Slide 27

Slide 27 text

def handle_cast( {:put_coin, coin}, state ) do {:noreply, update_in(state.credit, &add_coin/1)} end

Slide 28

Slide 28 text

def accepting_coins( :cast, {:put_coin, coin}, data ) do {:keep_state, update_in(data.credit, &add_coin/1)} end

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

def accepting_coins( {:call, from}, {:press_button, beverage}, data ) do if enough_credit?(data, beverage) do dispense_beverage(data, beverage) actions = [{:reply, from, :ok}] {:next_state, :dispensing, data, actions} else # ... end end

Slide 32

Slide 32 text

actions = [{:reply, from, :ok}] {:next_state, :dispensing, data, actions}

Slide 33

Slide 33 text

that’s kind of it?

Slide 34

Slide 34 text

Feature tour

Slide 35

Slide 35 text

Postponing events State enter callbacks Timeouts (state, event, named) Internal events Non-finite state machines

Slide 36

Slide 36 text

Postponing events

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

def dispensing(:cast, {:add_coin, _}, data) do actions = [:postpone] {:keep_state_and_data, actions} end def accepting_coins(:cast, {:add_coin, _}, data) do # Normal implementation end

Slide 39

Slide 39 text

“State enter”

Slide 40

Slide 40 text

def callback_mode do [ :state_functions, :state_enter ] end

Slide 41

Slide 41 text

def accepting_coins(:enter, _old_state, data) do update_display(data.credit) :keep_state_and_data end

Slide 42

Slide 42 text

Timeouts

Slide 43

Slide 43 text

Event timeouts

Slide 44

Slide 44 text

def accepting_coins(:cast, {:put_coin, coin}, data) do {:keep_state, update_in(data.credit, &add_coin/1)} end

Slide 45

Slide 45 text

def accepting_coins(:cast, {:put_coin, coin}, data) do actions = [ {:timeout, to_timeout(second: 30), :give_back_coins} ] {:keep_state, update_in(data.credit, &add_coin/1), actions} end def accepting_coins(:timeout, :give_back_coins, data)

Slide 46

Slide 46 text

{:timeout, to_timeout(second: 30), :give_back_coins} def accepting_coins(:timeout, :give_back_coins, data)

Slide 47

Slide 47 text

State timeouts

Slide 48

Slide 48 text

def dispensing(:enter, _old_state, _data) do actions = [ {:state_timeout, to_timeout(minute: 1), :stuck} ] {:keep_state_and_data, actions} end def dispensing(:state_timeout, :stuck, data) do # Call for help, the machine got stuck. end

Slide 49

Slide 49 text

Named timeouts

Slide 50

Slide 50 text

def init(options) do actions = [ {{:timeout, :self_health_check}, to_timeout(hour: 1), :no_content} ] {:ok, :idle, data, actions} end def idle({:timeout, :self_health_check}, _content, data) do # ... end

Slide 51

Slide 51 text

def accepting_coins({:timeout, :self_health_check}, _, _) do {:keep_state_and_data, [:postpone]} end def dispensing({:timeout, :self_health_check}, _, _) do {:keep_state_and_data, [:postpone]} end

Slide 52

Slide 52 text

{:timeout, ms, content} {:state_timeout, ms, content} {{:timeout, :some_name}, ms, content}

Slide 53

Slide 53 text

“Internal” events

Slide 54

Slide 54 text

def disconnected({:timeout, :reconnect}, _, _data) do {:keep_state_and_data, {:next_event, :internal, :connect}} end

Slide 55

Slide 55 text

Non-finite state machines

Slide 56

Slide 56 text

def callback_mode do :handle_event_function end

Slide 57

Slide 57 text

def accepting_coins( :cast, {:put_coin, coin}, data ) do # ... end

Slide 58

Slide 58 text

def handle_event( :cast, {:put_coin, coin}, :accepting_coins, data ) do # ... end

Slide 59

Slide 59 text

def handle_event( event_type, event_content, current_state, data )

Slide 60

Slide 60 text

def handle_event( :cast, {:put_coin, coin}, {:accepting_coins, credit}, data ) do new_state = {:accepting_coins, credit + coin} {:next_state, new_state, data} end

Slide 61

Slide 61 text

Use cases

Slide 62

Slide 62 text

No content

Slide 63

Slide 63 text

GenServer :gen_statem or

Slide 64

Slide 64 text

def handle_call(call, from, %{user: nil}) def handle_call(call, from, %{user: %User{}}) def handle_call(call, from, %{user: %Admin{}})

Slide 65

Slide 65 text

def no_user({:call, from}, _, _) def user({:call, from}, _, _) def admin({:call, from}, _, _)

Slide 66

Slide 66 text

Connections!

Slide 67

Slide 67 text

DBConnection Redix HTTP/2 (?)

Slide 68

Slide 68 text

No content

Slide 69

Slide 69 text

state/named timeouts -> reconnection w/ backoff event timeouts -> keepalive postponed events -> retrying requests

Slide 70

Slide 70 text

Workflow engines IoT sensors …

Slide 71

Slide 71 text

Wrapping up

Slide 72

Slide 72 text

Timeouts

Slide 73

Slide 73 text

No Elixir wrapper it’s fine

Slide 74

Slide 74 text

Data structures Processes vs

Slide 75

Slide 75 text

HTTP/2 stream state machine diagram

Slide 76

Slide 76 text

Composability

Slide 77

Slide 77 text

No content

Slide 78

Slide 78 text

Resources Wikipedia Docs

Slide 79

Slide 79 text

Thanks! @whatyouhide AlchemyConf 2025