3 ms·
iex(1)> a=10 10 iex(2)> a=11 11 Because underneath, it’s doing A0 = 10. A1 = 11. You might not like it, because it feels like mutation, b
by weatherlight 3y ago
iex(1)> a=10
10
iex(2)> a=11
11
Because underneath, it’s doing
A0 = 10.
A1 = 11.
You might not like it, because it feels like mutation, but it’s not, it’s rebinding. Just consider this as a syntactic sugar. It’s useful when doing
conn = conn |> apply_some_change()
In the end, it does generate valid bytecode for the BEAM, and immutability is respected.
BTW You might prefer Erlang syntax, but You would lose |>
José Valim, has a great writeup about this here.
https://blog.plataformatec.com.br/2016/01/comparing-elixir-and-erlang-variables/ https://blog.plataformatec.com.br/2016/01/comparing-elixir-a...
- killthebuddha 3y agoGenuine question: From an application developer's perspective, what's the difference between mutating a value and transparently rebinding an old name to a new value? Is it just that in the latter case other references don't pick up the changes? So with rebinding we don't have something like a = 10 b = a a = 11 print(b) // 11 ?
- OkayPhysicist 3y agodef func() do a = 10 def func2() do a + 1 end a = 20 func2() end Mutation would have func() return 21. Rebinding has it return 11. Likewise, mutating languages typically allow for a method to modify its arguments when those arguments are objects public void modify(MyObject a){ a.changed = true; } public bool test_modify(){ MyObject b = new MyObject(); b.changed = false; modify(b); return b.changed } test_modify will return true in languages that allow mutation.
- Miner49er 3y agoThis is a great example of why the pin-operator is bad, IMO. Rebinding isn't worth this complication.
- OkayPhysicist 3y agoOn the contrary. I'd like the pin operator regardless of whether rebinding is allowed or not. def func() do s = g() ...several lines of code l = t() case l do {s, q} -> #blah {q,q} -> #blah {p, r} -> #blah end end Without the "^s", you need context to determine "s"'s behavior. If s is currently unbound, then it'd be assignment. If it's unbound, it'd be pattern matching. It's a rare enough operation that it's nice to make it explicit. Erlang didn't have rebinding, and Elixir learned from the mistake. Rebinding is not complicated: the scope rules are simple, you learn them and then you're done. It's Day 1 type stuff, which is absolutely not worth optimizing for if you're trying to build a useful language. Without them, you end up coming up with a bunch of bogus variable names that don't add anything to the conversation. Imagine modifying an entry in a struct (or, more exactly, you're making a copy of a struct with one value changed) With rebinding, it's easy: def func(dict) do dict = dict |> Map.put("apple", 1) end Without rebinding, you need a pointless new variable name: def func(dict) do dict_with_one_apple = dict |> Map.put("apple", 1) end
- Miner49er 3y agoI do agree with the pin-operator, after more thought. I actually think it's good, it's just the rebinding that I don't like. I understand the argument for rebinding, as an Erlang developer I have to deal with not having it all the time. I guess I don't really see it as that big of a deal to have to use more variable names. The way I see it, if you're transforming something, it's fine for it to have a different name. I haven't used elixir, but I would guess pipelining covers 95% of the time that rebinding would be wanted, and the other 5% of the time I think I would prefer not to have it, but it's really just nitpicking, it's not too big of a deal.