What is the “pin” operator for, and are Elixir var

2019-01-17 03:09发布

Currently trying to understand the "^" operator in Elixir. From the website:

The pin operator ^ can be used when there is no interest in rebinding a variable but rather in matching against its value prior to the match:

Source - http://elixir-lang.org/getting_started/4.html

With this in mind, you can attach a new value to a symbol like so:

iex> x = 1  # Outputs "1"
iex> x = 2  # Outputs "2"

I can also do:

iex> x = x + 1  # Outputs "3"!

So my first question is; Are Elixir variables mutable? It sure looks like if that's the case... Shouldn't that be possible in a functional programming language?

So now we come to the "^" operator...

iex> x = 1  # Outputs "1"
iex> x = 2  # Outputs "2"
iex> x = 1  # Outputs "1"
iex> ^x = 2 # "MatchError"
iex> ^x = 1  # Outputs "1"

I think the effect of "^" is to lock "x" to the last value binded to it. Is that all there is to it? Why not just ensure that all 'matches'/assignments are immutable like Erlang itself?

I was just getting used to that...

2条回答
霸刀☆藐视天下
2楼-- · 2019-01-17 03:31

The data in Elixir is still immutable, but there are couple of shorthands, that let you type less or don't worry about finding new names. In Erlang, you could often see code like this:

SortedList = sort(List),
FilteredList = filter(SortedList),
List3 = do_something_with(FilteredList),
List4 = another_thing_with(List3)

In Elixir, you could just write:

list = sort(list)
list = filter(list)
list = do_something_with(list)
list = another_thing_with(list)

This is exactly the same, but it looks a little better. Of course the best solutions would be to write like this:

list |> sort |> filter |> do_something |> another_thing_with

Every time, you assign new thing to list variable, you get new instance:

iex(1)> a = 1
1
iex(2)> b = [a, 2]
[1, 2]
iex(3)> a = 2
2
iex(4)> b
[1, 2] # first a did not change, it is immutable, currently a just points to something else

You just say, that you are no longer interested in the old a and let it point to something else. If you are coming from Erlang background, you probably know the f function from shell.

A = 1.
f(A).
A = 2.

In Elixir you just don't have to write the f. It is done automatically for you. This means, that every time, you have variable on the left side of the pattern match, you are assigning new value to it.

Without the ^ operator, you wouldn't be able to have a variable on the left side of pattern match, because it would get new value from the right side. ^ means do not assign new things to this variable - treat it as a literal value.

That is why in Elixir

x = 1
[1, x, 3] = [1, 2, 3]

is equivalent in Erlang to:

X = 1,
[1, CompletelyNewVariableName, 3] = [1, 2, 3]

and:

x = 1
[1, ^x, 3] = [1, 2, 3]

is equivalent to:

x = 1
[1, 1, 3] = [1, 2, 3]

which in Erlang is:

X = 1,
[1, X, 3] = [1, 2, 3]
查看更多
看我几分像从前
3楼-- · 2019-01-17 03:36

Data in elixir is immutable, variables though are re-assignable. What can make elixir slightly confusing is the combined assignment and pattern matching that you are seeing.

When you use the equals sign with a variable reference on the left elixir will first pattern match the structure, and then perform an assignment. When you have just a sole variable reference on the left, it will match any structure and so will be assigned like so:

 a = 1 # 'a' now equals 1
 a = [1,2,3,4] # 'a' now equals [1,2,3,4]
 a = %{:what => "ever"} # 'a' now equals %{:what => "ever"}

When you have a more complex structure on the left elixir will first pattern match the structures, then perform the assignment.

[1, a, 3] = [1,2,3] 
# 'a' now equals 2 because the structures match
[1, a] = [1,2,3] 
# **(MatchError)** because the structures are incongruent. 
# 'a' still equals it's previous value

If you want to value match against the contents of a variable you can use the pin '^':

a = [1,2] # 'a' now equals [1,2]
%{:key => ^a} = %{:key => [1,2]} # pattern match successful, a still equals [1,2]
%{:key => ^a} = %{:key => [3,4]} # **(MatchError)**

This contrived example could also have been written with 'a' on the right hand side and without the pin:

%{:key => [1,2]} = %{:key => a}

Now say you wanted to assign a variable to part of a structure but only if part of that structure matched something stored in 'a', in elixir this is trivial:

a = %{:from => "greg"}
[message, ^a] = ["Hello", %{:from => "greg"}] # 'message' equals "Hello"
[message, ^a] = ["Hello", %{:from => "notgreg"}] # **(MatchError)**

In these simple examples the use of the pin and pattern matching isn't immediately super valuable, but as you learn more elixir and start pattern matching more and more it becomes part of the expressiveness that elixir affords.

查看更多
登录 后发表回答