Reputation: 903
Complete Elixir beginner. I am sure I am missing some fundamental issues in the following. Can someone please explain, exactly why the second code not work as the first one?
defmodule Bulb do
defstruct [:state]
def turn_on(bulb = %Bulb{}) do
%{bulb | state: :on}
end
def turn_off(bulb = %Bulb{}) do
%{bulb | state: :off}
end
end
bulb = %Bulb{state: :on} # --> %Bulb{state: :on}
bulb |> Bulb.turn_off() # --> %Bulb{state: :off}
bulb |> Bulb.turn_on() # --> %Bulb{state: :on}
versus
defmodule Bulb do
defstruct [:state]
def switch_bulb(bulb = %Bulb{}) do
case bulb do
%{state: :off} -> %{bulb | state: :on}
%{state: :on} -> %{bulb | state: :off}
end
end
end
bulb = %Bulb{state: :on} # --> %Bulb{state: :on}
bulb |> Bulb.switch_bulb() # --> %Bulb{state: :off}
bulb |> Bulb.switch_bulb() # --> %Bulb{state: :off} why?
(Yes, the second one also works if I do the following.)
defmodule Bulb do
defstruct [:state]
def switch_bulb(bulb = %Bulb{}) do
case bulb do
%{state: :off} -> %{bulb | state: :on}
%{state: :on} -> %{bulb | state: :off}
end
end
end
bulb = %Bulb{state: :on} # --> %Bulb{state: :on}
bulb = bulb |> Bulb.switch_bulb() # --> %Bulb{state: :off}
bulb = bulb |> Bulb.switch_bulb() # --> %Bulb{state: :on}
Upvotes: 0
Views: 73
Reputation: 121010
In elixir, everything is immutable. bulb |> Bulb.turn_on()
from the first snippet does not do what you think it does. It does literally nothing, actually, “switching” the state from on
(initial value) to on
.
To understand, what happens, do print bulb
every time.
bulb = %Bulb{state: :on} # %Bulb{state: :on}
bulb |> IO.inspect() |> Bulb.turn_off() |> IO.inspect() # on → off
bulb |> IO.inspect() |> Bulb.turn_on() |> IO.inspect() # on → on
bulb
from inside switch_bulb/1
function has the same name, but a very different scope, it’s not the same variable. bulb
stays what it was assigned to.
bulb = %Bulb{state: :on} # --> %Bulb{state: :on}
IO.inspect(bulb) # --> %Bulb{state: :on}
bulb |> Bulb.switch_bulb() # --> %Bulb{state: :off}
bulb # --> %Bulb{state: :on}
bulb |> Bulb.switch_bulb() # --> %Bulb{state: :off} why?
Even more: the example below won’t change the value of outermost bulb
too
bulb = :on
if true, do: bulb = :off # different scope, outermost `bulb` is untouched
IO.inspect(bulb) #⇒ :on
That said unless you reassign the value of any expression, it simply gets lost. Nothing is mutated in place.
BTW, =
is NOT an assignment, it’s a matching operator. In this case, it’s rebinding the variable.
Upvotes: 2