Elixir 변수는 정말 불변입니까?
Dave Thomas의 저서 Programming Elixir에서 그는 "Elixir는 불변 데이터를 시행합니다"라고 말하고 계속해서 다음과 같이 말합니다.
Elixir에서 변수가 [1,2,3]과 같은 목록을 참조하면 변수를 리 바인드 할 때까지 항상 동일한 값을 참조한다는 것을 알고 있습니다.
이것은 "변경하지 않는 한 변경되지 않을 것"처럼 들리므로 변경 가능성과 리 바인딩의 차이가 무엇인지 혼란 스럽습니다. 차이점을 강조하는 예가 정말 도움이 될 것입니다.
불변성은 데이터 구조가 변경되지 않음을 의미합니다. 예를 들어 함수 HashSet.new
는 빈 집합을 반환하며 해당 집합에 대한 참조를 유지하는 한 비어 있지 않은 상태가되지 않습니다. Elixir에서 할 수있는 일은 무언가에 대한 변수 참조를 버리고 새로운 참조에 리 바인딩하는 것입니다. 예를 들면 :
s = HashSet.new
s = HashSet.put(s, :element)
s # => #HashSet<[:element]>
무엇을 할 수없는 일이 당신이 명시 적으로 리 바인딩없이 변경 해당 참조 아래에있는 값입니다 :
s = HashSet.new
ImpossibleModule.impossible_function(s)
s # => #HashSet<[:element]> will never be returned, instead you always get #HashSet<[]>
이를 Ruby와 비교하면 다음과 같은 작업을 수행 할 수 있습니다.
s = Set.new
s.add(:element)
s # => #<Set: {:element}>
Elixir의 "변수"를 명령형 언어의 변수, "값을위한 공간"으로 생각하지 마십시오. 오히려 "값에 대한 레이블"로 간주하십시오.
Erlang에서 변수 ( "라벨")가 어떻게 작동하는지 살펴보면 더 잘 이해할 수있을 것입니다. "레이블"을 값에 바인딩 할 때마다 항상 바인딩 된 상태로 유지됩니다 (물론 범위 규칙이 여기에 적용됨).
Erlang에서는 다음과 같이 작성할 수 없습니다 .
v = 1, % value "1" is now "labelled" "v"
% wherever you write "1", you can write "v" and vice versa
% the "label" and its value are interchangeable
v = v+1, % you can not change the label (rebind it)
v = v*10, % you can not change the label (rebind it)
대신 다음을 작성해야합니다.
v1 = 1, % value "1" is now labelled "v1"
v2 = v1+1, % value "2" is now labelled "v2"
v3 = v2*10, % value "20" is now labelled "v3"
보시다시피 이것은 주로 코드 리팩토링을 위해 매우 불편합니다. 첫 번째 줄 뒤에 새 줄을 삽입하려면 모든 v * 번호를 다시 매기거나 "v1a = ..."와 같이 작성해야합니다.
따라서 Elixir에서는 주로 편의를 위해 변수를 리 바인드 할 수 있습니다 ( "라벨"의 의미 변경).
v = 1 # value "1" is now labelled "v"
v = v+1 # label "v" is changed: now "2" is labelled "v"
v = v*10 # value "20" is now labelled "v"
요약 : 명령형 언어에서 변수는 이름이 지정된 가방과 같습니다. "v"라는 이름의 가방이 있습니다. 처음에는 샌드위치를 넣습니다. 사과를 넣는 것보다 (샌드위치가 없어져서 쓰레기 수거 기가 먹었을 수도 있습니다). Erlang과 Elixir에서 변수는 무언가를 넣을 곳 이 아닙니다 . 단지 값 의 이름 / 라벨 일뿐 입니다. Elixir에서 레이블의 의미를 변경할 수 있습니다. 얼랭에서는 할 수 없습니다. 그것이 변수가 공간을 차지하지 않기 때문에 Erlang 또는 Elixir에서 "변수에 대한 메모리 할당"이 의미가없는 이유입니다. 가치가 있습니다. 이제 그 차이를 분명히 볼 수있을 것입니다.
더 깊이 파고 싶다면 :
1) "unbound"및 "bound"변수가 Prolog에서 어떻게 작동하는지 살펴보십시오. 이것이 "변하지 않는 변수"라는 약간 이상한 Erlang 개념의 근원입니다.
2) Erlang의 "="는 실제로 할당 연산자가 아니라 일치 연산자입니다! 바인딩되지 않은 변수를 값과 일치시킬 때 변수를 해당 값에 바인딩합니다. 바인딩 된 변수를 일치시키는 것은 바인딩 된 값을 일치시키는 것과 같습니다. 따라서 일치 오류가 발생합니다.
v = 1,
v = 2, % in fact this is matching: 1 = 2
3) Elixir에서는 그렇지 않습니다. 따라서 Elixir에는 일치를 강제하는 특수 구문이 있어야합니다.
v = 1
v = 2 # rebinding variable to 2
^v = 3 # matching: 2 = 3 -> error
Erlang and obviously Elixir that is built on top of it, embraces immutability. They simply don’t allow values in a certain memory location to change. Never Until the variable gets garbage collected or is out of scope.
Variables aren't the immutable thing. The data they point to is the immutable thing. That's why changing a variable is referred to as rebinding.
You're point it at something else, not changing the thing it points to.
x = 1
followed by x = 2
doesn't change the data stored in computer memory where the 1 was to a 2. It puts a 2 in a new place and points x
at it.
x
is only accessible by one process at a time so this has no impact on concurrency and concurrency is the main place to even care if something is immutable anyway.
Rebinding doesn’t change the state of an object at all, the value is still in the same memory location, but it’s label (variable) now points to another memory location, so immutability is preserved. Rebinding is not available in Erlang, but while it is in Elixir this is not braking any constraint imposed by the Erlang VM, thanks to its implementation. The reasons behind this choice are well explained by Josè Valim in this gist .
Let's say you had a list
l = [1, 2, 3]
and you had another process that was taking lists and then performing "stuff" against them repeatedly and changing them during this process would be bad. You might send that list like
send(worker, {:dostuff, l})
Now, your next bit of code might want to update l with more values for further work that's unrelated to what that other process is doing.
l = l ++ [4, 5, 6]
Oh no, now that first process is going to have undefined behavior because you changed the list right? Wrong.
That original list remains unchanged. What you really did was make a new list based on the old one and rebind l to that new list.
The separate process never has access to l. The data l originally pointed at is unchanged and the other process (presumably, unless it ignored it) has its own separate reference to that original list.
What matters is you can't share data across processes and then change it while another process is looking at it. In a language like Java where you have some mutable types (all primitive types plus references themselves) it would be possible to share a structure/object that contained say an int and change that int from one thread while another was reading it.
In fact, it's possible to change a large integer type in java partially while it's read by another thread. Or at least, it used to be, not sure if they clamped that aspect of things down with the 64 bit transition. Anyway, point is, you can pull the rug out from under other processes/threads by changing data in a place that both are looking at simultaneously.
That's not possible in Erlang and by extension Elixir. That's what immutability means here.
To be a bit more specific, in Erlang (the original language for the VM Elixir runs on) everything was single-assignment immutable variables and Elixir is hiding a pattern Erlang programmers developed to work around this.
In Erlang, if a=3 then that was what a was going to be its value for the duration of that variable's existence until it dropped out of scope and was garbage collected.
This was useful at times (nothing changes after assignment or pattern match so it is easy to reason about what a function is doing) but also a bit cumbersome if you were doing multiple things to a variable or collection over the course executing a function.
Code would often look like this:
A=input,
A1=do_something(A),
A2=do_something_else(A1),
A3=more_of_the_same(A2)
This was a bit clunky and made refactoring more difficult than it needed to be. Elixir is doing this behind the scenes, but hiding it from the programmer via macros and code transforms performed by the compiler.
The variables really are immutable in sense, every new rebinding (assignment) is only visible to access that come after that. All previous access, still refer to old value(s) at the time of their call.
foo = 1
call_1 = fn -> IO.puts(foo) end
foo = 2
call_2 = fn -> IO.puts(foo) end
foo = 3
foo = foo + 1
call_3 = fn -> IO.puts(foo) end
call_1.() #prints 1
call_2.() #prints 2
call_3.() #prints 4
참고URL : https://stackoverflow.com/questions/29967086/are-elixir-variables-really-immutable
'IT박스' 카테고리의 다른 글
Java Enum 반환 Int (0) | 2020.11.13 |
---|---|
ng-cloak 지시문을 올바르게 사용하는 방법은 무엇입니까? (0) | 2020.11.13 |
C # "finally"블록이 항상 실행됩니까? (0) | 2020.11.13 |
내 텍스트 영역의 자리 표시자가 표시되지 않는 이유는 무엇입니까? (0) | 2020.11.13 |
기본 tensorflow 예제 실행 오류 (0) | 2020.11.13 |