I do not know the full code, but it seems as if the value of player is used to determine which player is to update, your alternate code does seem to update only player1 ever.
An alternative code that would use update syntax had to look more like this:
def check(%Rules{state: :players_set} = rules, {:set_islands, player}) do
rules = case player do
:player1 -> %{rules | player1: :islands_set}
:player2 -> %{rules | player2: :islands_set}
end
case both_players_islands_set?(rules) do
true -> {:ok, %Rules{rules | state: :palyer1_turn}}
false -> {:ok, rules}
end
end
To be honest, the authors way of using Map.put/3 is also less safe. It might invalidate the struct:
iex(1)> defmodule S do
...(1)> defstruct [:foo]
...(1)> end
iex(2)> s = %S{}
%S{foo: nil}
iex(3)> s = %{s | foo: :bar}
%S{foo: :bar}
iex(4)> s = Map.put(s, :bar, :foo)
%{__struct__: S, bar: :foo, foo: :bar}
iex(5)> s2 = %S{}
%S{foo: nil}
iex(6)> s2 = %{s2 | bar: :foo}
** (KeyError) key :bar not found in: %S{foo: nil}
(stdlib) :maps.update(:bar, :foo, %S{foo: nil})
(stdlib) erl_eval.erl:259: anonymous fn/2 in :erl_eval.expr/5
(stdlib) lists.erl:1263: :lists.foldl/3
Therefore I consider Map.replace!/3 a better replacement for the update syntax, as it gives us the dynamic approach we need while still guaranteeing to not invalidate the struct.
It will actually compile, but fail at runtime. As at compiletime elixir doesn’t know that s has not a key :foo.
As I showed already, it can’t use the map update syntax without a lot of additional repition, as the key is from a variable but the update syntax requires you to know the key at compiletime.