4 ms·
For me it would be the Pin-Operator. Which is only needed cause variables can "mutate". IMHO it's not that common that we need to "reassign" variables, we could
by salzig 3y ago
For me it would be the Pin-Operator. Which is only needed cause variables can "mutate". IMHO it's not that common that we need to "reassign" variables, we could life without the looks-like-reassignment.
I touched Erlang before, it's hard to get my brain to accept elixir is different in regards of variables :)
- weatherlight 3y agoiex(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.
- marcandre 3y agoInteresting. In our codebase we do this all the time. A quick search revealed 280 occurrences of `some_var = some_var |> ...`. 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.
- arrowsmith 3y agoYes, good luck writing a non-trivial Phoenix and/or LiveView app without ever writing `socket = something(socket, …)` or `assigns = assign(assigns, …)`. 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.
- lolinder 3y agoNitpick of the nitpick: name shadowing is one of the most important QOL improvements a functional language can add. The pipe operator can alleviate some of the pain of lack of shadowing, but sometimes you really do want to have a chain of transformations to a value that don't fit well as a pipe, such as when there's more than one intermediate value being used in parallel. In those situations, forcing programmers to give each intermediate value a new name each time is pointless overhead and doesn't actually improve "purity" in any meaningful way.
- lvass 3y agoI love how pins make it explicit you are not assigning something without having to look at the context. I prefer it being there even if you couldn't shadow a variable.