Everything I\'ve read about Elixir says that assignment should be thought of as pattern matching. If so then why does x = x + 1 work in Elixir? There is no value of x for wh
Everything I've read about Elixir says that assignment should be thought of as pattern matching.
In Elixir, =
is called the pattern match operator, but it does not work the same way as the pattern match operator in Erlang. That's because in Elixir variables are not single assignment like they are in Erlang. Here's the way Erlang works:
~/erlang_programs$ erl
Erlang/OTP 20 [erts-9.3] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:10] [hipe] [kernel-poll:false]
Eshell V9.3 (abort with ^G)
1> X = 15.
15
2> X = 100.
** exception error: no match of right hand side value 100
3> X.
15
4>
Therefore, in Erlang this fails:
4> X = X + 1.
** exception error: no match of right hand side value 16
Things are pretty simple with Erlang's single assignment: because X already has a value, the line X = X + 1
cannot be an attempt to assign a new value to X, so that line is an attempt to pattern match (15 = 15 + 1
), which will always fail.
On the other hand, in Elixir variables are not single assignment:
Interactive Elixir (1.6.6) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> x = 15
15
iex(2)> x = 100
100
iex(3)> x
100
iex(4)>
The fact that variables are not single assignment in Elixir means that Elixir needs to make a choice when you write:
x = 10
x = x + 1 #or even x = 15
Choice 1) Should the second line be interpreted as assignment to x
?
Choice 2) Should the second line be interpreted as an attempt to pattern match (i.e. 10 = 11
)?
Elixir goes with Choice 1. That means that actually performing a pattern match with the so called pattern match operator in Elixir is more difficult: you have to use the pin operator(^
) in conjunction with the match operator(=
):
x = 10
^x = x + 1
Now, the second line will always fail. There is also a trick that will work in some situations if you want to perform pattern matching without using the pin operator:
x = 10
12 = x
In the second line, you put the variable on the right hand side. I think the rule can be stated like this: On the right hand side of the pattern match operator(=
), variables are always evaluated, i.e. replaced with their values. On the left hand side variables are always assigned to--unless the pin operator is used, in which case a pinned variable is replaced by its current value and then pattern matched against the right hand side. As a result, it's probably more accurate to call Elixir's =
operator a hybrid assignment/pattern match operator.
You can imagine x = x + 1
being rewritten by the compiler to something like x2 = x1 + 1
.
This is pretty close to how it works. It's not a simple index number like I used here, but the concept is the same. The variables seen by the BEAM are immutable, and there is no rebinding going on at that level.
In Erlang programs, you'll find code like X2 = X1 + 1
all over. There are downsides to both approaches. José Valim made a conscious choice to allow rebinding of variables when he designed Elixir, and he wrote a blog post comparing the two approaches and the different bugs you run the risk of:
http://blog.plataformatec.com.br/2016/01/comparing-elixir-and-erlang-variables/
During the pattern matching, the values on the right of the match are assigned to their matched variables on the left:
iex(1)> {x, y} = {1, 2}
{1, 2}
iex(2)> x
1
iex(3)> y
2
On the right hand side, the values of the variables prior to the match are used. On the left, the variables are set.
You can force the left side to use a variable's value too, with the ^ pin operator:
iex(4)> x = 1
1
iex(5)> ^x = x + 1
** (MatchError) no match of right hand side value: 2
This fails because it's equivalent to 1 = 1 + 1
, which is the failure condition you were expecting.