The scoping punctuation is very easy if you think about it. I begrudgingly accept “I don’t like the syntax” as a reason to not use Erlang, but there’s nothing challenging about it. It’s a much smaller syntax than Elixir.
The punctuation rules are very similar to English: comma means you’re not through with your thought yet, semi-colon means you’re introducing a new clause, period means you’re done with the entire thought.
The punctuation rules are very similar to English: comma means you’re not through with your thought yet, semi-colon means you’re introducing a new clause, period means you’re done with the entire thought.
Agree, at least when looking at code statically. It's a bit of an inconvenience when editing though. For example, re-ordering lines in a function / adding a new line to the end. That leaves a full stop at the end of an intermediate line and/or a semi-colon at the end. Compiling/running that without fixing (assuming you didn't spot it) can lead to cryptic compiler errors.
I find this hits me a surprisingly large number of times. Maybe it's just the way I write code. But it is an annoyance (for me anyway).
To be fair, any decent editor/IDE will highlight the error. None that I'm aware of has an auto-correct though.
It's a minor annoyance in the great scheme of things. Erlang remains one of my favourite languages. Writing REST services as OTP applications on top of Cowboy* is a very rewarding experience and generates rock-solid, highly scalable, production-quality binaries.
*Webmachine was even better though sadly now not actively maintained.
Just to clarify: Values in elixir are still immutable.
The core team will insist that variables are immutable but bindings are mutable, but in my mind variables are their bindings (there is not a third category distinct from the value and the lexical binding).
I think there is huge debate as to whether this is good or bad - I think though you really really want to have mutable variables in the REPL. In your static code it's trivial to check for variable rebinding and be sure you're not doing it (credo makes this easy to set up)
The core team will insist that variables are immutable but bindings are mutable
The documentation on the Elixir website refers to it as rebinding, and I don't see any references to bindings being considered mutable.
Rebinding means you've created a new binding with the same name. The original isn't being mutated, it's just not accessible anymore because of the scoping rules.
My point is I don't know what that means. The variable is the binding changing the binding by throwing away the old one is not distinct from mutating it because the binding "isn't a real thing, just conceptual", just as the variable isn't "a real thing"; unlike a value which is actually a thing sitting in a memory slot in your vm. If you can give me an example of where there is a meaningful distinction between the variable and its lexical binding I am happy to be enlightened.
A mutable variable is a memory slot that can be mutated, though.
No, that's a mutable value.
The variable is the lexical representation of a value. If you change which value your variable binds to, you have changed the representation of the variable; you have mutated it. The underlying values in memory have not changed, they are immutable.
The variable is the lexical representation of a value. If you change which value your variable binds to, you have changed the representation of the variable; you have mutated it. The underlying values in memory have not changed, they are immutable.
I completely agree with you that values and variables are separate things. However, mutable variables are still memory slots that can be mutated, while immutable variables are not. The memory slot of a variable usually lives on the stack, and contains a pointer to a value that lives elsewhere in memory.
I think the difference will be clearer in a language that distinguishes between introducing a variable and setting a variable.
var foo = "abcd"; // introduce a new variable binding
foo := "xyz"; // set a variable
Here's an example of what you can do in a language that has mutable variables that you cannot do in a language that only supports shadowing bindings:
var strings = ["str1", "str2", "str3"];
var foo = "abcd";
for (string in strings) {
foo := string;
}
println(foo); // Prints out "str3"
In this case, foo itself is a memory slot that can be mutated, independently of the strings, which are immutable values. foo is a mutable slot in memory that can be updated to contain different pointer values.
Now, if you only have shadowing, you end up with this:
var strings = ["str1", "str2", "str3"];
var foo = "abcd";
for (string in strings) {
var foo = string;
}
println(foo); // Prints out "abcd"
Each iteration of the loop is introducing a new binding with the name foo, but it is not changing or removing the original binding of foo. It's just that within the loop, only the local binding is visible.
I think part of the confusion around Elixir is that it only supports introducing new bindings, but the syntax looks like it's setting the value of a variable.
Another example that will hopefully help clarify:
var foo = "abcd";
var bar = "xyz";
println(bar);
That example is nearly the same as this (and in terms of what the program does, it's identical):
var foo = "abcd";
var foo = "xyz";
println(foo);
The only difference is that in the second case, the first foo binding isn't visible after the second one has been introduced. At least conceptually, they both still exist, but because of scoping rules, there's no way to access the first one.
I believe an example of where binding differs from assignment is if you define a function which references a binding in the outer scope, the create a new binding with the same name in the outer scope, the value inside the function does not change:
a = 1
f = fn -> a end
f.() # returns 1
a = 2
f.() # still returns 1
This feels like flawed thinking to me. I know what you're saying, but claiming that variables are mutable would lead me to believe we could have things like counters. I would say it's best to think of variables as the memory address of their binding. We are allow to rebind the name. That's how I see it, at least.
That's not at all the traditional definition of variable. A variable is the code-lexical representation of a value in memory. (Think "variable" in algebra, which is the formula-lexical representation of an abstract value).
Anyways we can't have traditional counters, because of 1) lexical scoping rules and 2) value immutability.
Comments
Give Elixir a spin.
I'd second that, Elixir does away with the sometimes vexing comma and dot syntax used for scoping. Also, it does have mutable variables.
You do have to get to grips with pattern matching and processes as objects being such core concepts, but I found that once I got it, it is wonderful.
The scoping punctuation is very easy if you think about it. I begrudgingly accept “I don’t like the syntax” as a reason to not use Erlang, but there’s nothing challenging about it. It’s a much smaller syntax than Elixir.
The punctuation rules are very similar to English: comma means you’re not through with your thought yet, semi-colon means you’re introducing a new clause, period means you’re done with the entire thought.
Agree, at least when looking at code statically. It's a bit of an inconvenience when editing though. For example, re-ordering lines in a function / adding a new line to the end. That leaves a full stop at the end of an intermediate line and/or a semi-colon at the end. Compiling/running that without fixing (assuming you didn't spot it) can lead to cryptic compiler errors.
I find this hits me a surprisingly large number of times. Maybe it's just the way I write code. But it is an annoyance (for me anyway).
To be fair, any decent editor/IDE will highlight the error. None that I'm aware of has an auto-correct though.
It's a minor annoyance in the great scheme of things. Erlang remains one of my favourite languages. Writing REST services as OTP applications on top of Cowboy* is a very rewarding experience and generates rock-solid, highly scalable, production-quality binaries.
*Webmachine was even better though sadly now not actively maintained.
Just to clarify: Values in elixir are still immutable.
The core team will insist that variables are immutable but bindings are mutable, but in my mind variables are their bindings (there is not a third category distinct from the value and the lexical binding).
I think there is huge debate as to whether this is good or bad - I think though you really really want to have mutable variables in the REPL. In your static code it's trivial to check for variable rebinding and be sure you're not doing it (credo makes this easy to set up)
Variables in Elixir are not mutable, they can just be rebound. Think 'let' in Scheme or ML.
I was responding to this:
The documentation on the Elixir website refers to it as rebinding, and I don't see any references to bindings being considered mutable.
Rebinding means you've created a new binding with the same name. The original isn't being mutated, it's just not accessible anymore because of the scoping rules.
My point is I don't know what that means. The variable is the binding changing the binding by throwing away the old one is not distinct from mutating it because the binding "isn't a real thing, just conceptual", just as the variable isn't "a real thing"; unlike a value which is actually a thing sitting in a memory slot in your vm. If you can give me an example of where there is a meaningful distinction between the variable and its lexical binding I am happy to be enlightened.
In a scenario where you're just rebinding, you can't do this, for example (pseudo code):
It would always create a new foo binding in the for loop with value (12 + x);I think using Lisp syntax might help clarify, because it makes the binding scope explicit:
> unlike a value which is actually a thing sitting in a memory slot in your vm.A mutable variable is a memory slot that can be mutated, though.
No, that's a mutable value.
The variable is the lexical representation of a value. If you change which value your variable binds to, you have changed the representation of the variable; you have mutated it. The underlying values in memory have not changed, they are immutable.
I'm not crazy that this is a real definition: https://docs.julialang.org/en/v1/manual/variables/#man-varia...
I completely agree with you that values and variables are separate things. However, mutable variables are still memory slots that can be mutated, while immutable variables are not. The memory slot of a variable usually lives on the stack, and contains a pointer to a value that lives elsewhere in memory.
I think the difference will be clearer in a language that distinguishes between introducing a variable and setting a variable.
Here's an example of what you can do in a language that has mutable variables that you cannot do in a language that only supports shadowing bindings: In this case, foo itself is a memory slot that can be mutated, independently of the strings, which are immutable values. foo is a mutable slot in memory that can be updated to contain different pointer values.Now, if you only have shadowing, you end up with this:
Each iteration of the loop is introducing a new binding with the name foo, but it is not changing or removing the original binding of foo. It's just that within the loop, only the local binding is visible.I think part of the confusion around Elixir is that it only supports introducing new bindings, but the syntax looks like it's setting the value of a variable.
Another example that will hopefully help clarify:
That example is nearly the same as this (and in terms of what the program does, it's identical): The only difference is that in the second case, the first foo binding isn't visible after the second one has been introduced. At least conceptually, they both still exist, but because of scoping rules, there's no way to access the first one.I believe an example of where binding differs from assignment is if you define a function which references a binding in the outer scope, the create a new binding with the same name in the outer scope, the value inside the function does not change: a = 1 f = fn -> a end f.() # returns 1 a = 2 f.() # still returns 1
This feels like flawed thinking to me. I know what you're saying, but claiming that variables are mutable would lead me to believe we could have things like counters. I would say it's best to think of variables as the memory address of their binding. We are allow to rebind the name. That's how I see it, at least.
That's not at all the traditional definition of variable. A variable is the code-lexical representation of a value in memory. (Think "variable" in algebra, which is the formula-lexical representation of an abstract value).
Anyways we can't have traditional counters, because of 1) lexical scoping rules and 2) value immutability.
If your problem is errors then Elixir can still be pretty confusing, especially with respect to things like GenServer.