Are Elixir structs really immutable?

469 views Asked by At

I'm currently studying Elixir and I'm reading "Functional Web Development with Elixir, OTP and Phoenix", which imo is a great book. Working at the state machine chapter, I came up with the following code:

defmodule IslandsEngine.Rules do
  alias __MODULE__

  defstruct state: :initialized

  def new(), do: %Rules{}

  def check(%Rules{state: :initialized} = rules, :add_player), do:
    {:ok, %Rules{rules | state: :players_set}}

  def check(_state, _action), do: :error

end

The code above should work as a fully functional state machine. I'll paste above a few of iex commands:

iex(1)> alias IslandsEngine.Rules
IslandsEngine.Rules

iex(2)> rules = Rules.new()
%IslandsEngine.Rules{state: :initialized}

iex(3)> {:ok, rules} = Rules.check(rules, :add_player)
{:ok, %IslandsEngine.Rules{state: :players_set}}

iex(4)> rules.state
:players_set

So as you can see, the state struct has changed from :initialized to :add_player. Great.

My question is: is state: struct really immutable? I mean, the method check/1 returns a copy of the struct with a state: :players_set statement, which follows a correct functional pattern... but how does it "overwrite" the current status without modifying it directly?

Thanks a lot!

1

There are 1 answers

0
Sheharyar On BEST ANSWER

Elixir data-structures are indeed immutable. But what happens is that function calls return a completely new value (that is different from the original depending on the function you called).

As for the "changing the variable's value", that's an added feature in Elixir (over the original Erlang language). The variable's value doesn't actually change, it's just rebinded to the new one. The old ones are automatically garbage collected by the Erlang VM.


So in your example:

# This returns a completely new `%Rules{}` struct and rebinds
# the `rules` variable to the new term
{:ok, rules} = Rules.check(rules, :add_player)