I touched Erlang before, it's hard to get my brain to accept elixir is different in regards of variables :)
I touched Erlang before, it's hard to get my brain to accept elixir is different in regards of variables :)
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-a...
a = 10
b = a
a = 11
print(b) // 11
? def 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. 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)
endI 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.
I also find the pin operator much more readable, as the meaning of `{foo, ^bar} = result` doesn't require to know the context. `foo` is being assigned, `bar` is being matched on. No need to know the code before this line to interpret it.
I do remember finding the pin operator confusing when I first started learning Elixir, probably because I'd never seen anything like it in another language. But the confusion didn't last long; it's really not hard to understand. I've never felt like the pin operator was bad for readability.