Instead of talking about _variable bindings_, would it be better to use the term _variables_ (like other languages) and explain the rest in terms of references?
Let's explore. Are variable bindings:
Variable (mathematics): _(from Wikipedia)_
In elementary mathematics, a variable is an alphabetic character representing a number, called the value of the variable, which is either arbitrary or not fully specified or unknown.
Variable (computer science): _(from Wikipedia)_
In computer programming, a variable or scalar is a storage location paired with an associated symbolic name (an identifier), which contains some known or unknown quantity of information referred to as a value. The variable name is the usual way to reference the stored value; this separation of name and content allows the name to be used independently of the exact information it represents. The identifier in computer source code can be bound to a value during run time, and the value of the variable may thus change during the course of program execution.
Variable: _(my definition)_
A symbolic name that represents a _value_ or _reference_.
Niko Matsakis uses the terms variables and references in his into_rust() videos and I'm not confused. Even rustc's error messages use the term _variable_.
So what is a _variable binding_ and how is it different from a _variable_? In writing about this, it became clear that after 2 years following Rust, I still don't _know_. Some possibilities:
Am I missing an aspect to the _variable bindings_ concept?
I think saying a variable is _bound to a value_ or _bound to a reference_ is a great way of expressing it, but does it really mean something different to a variable _contains a value_ or _contains a reference_?
Does a distinction between _variable bindings_ and _variables_ need to be drawn?
Thank you for the detailed and thoughtful issue! 鉂わ笍 I am not confident in the subtleties involved here, so I'm going to let @steveklabnik speak to this!
I prefer speaking about bindings. There's other important things _bound_ by introducing a variable.
(I outlined such a case here: https://lobste.rs/s/oxuwzf/rust_vs_c_fine_grained_performance/comments/0bhl8y#c_0bhl8y)
To summarise:
let mut sink = io::BufWriter::new(io::stdout().lock());
is very different from
let stdout = io::stdout();
let mut sink = io::BufWriter::new(stdout.lock());
I prefer speaking about bindings whenever possible for that reason.
If we talk about introducing a "binding", this makes is clearer that additional things happen by saying "let".
I think of these terms as being interchangeable, even though I've been told they are not. I think previous versions of the book used these terms in an ambiguously interchangeable way as well.
@skade's comment about illuminating what else is created by the let makes sense, but isn't true in my experience. I just don't know the meaningful difference between the two terms.
I've always considered a variable binding to be syntactic and a variable to be semantic; aka. let x = y is a variable binding where x is the variable.
Yes, that makes sense to me also - using "binding" to mean "the creating of the variable with a let statement." But I don't think that's how its used in the context of PL theory in general.
A variable implies changeability/mutability, so we need a different concept to make immutability clear.
I disagree. The consistent & orthogonally useful terminology I prefer to use is:
const (or static when interior mutability doesn't apply)&STATIC as *const _ as usizestatic (global) or bindings in patterns (local)match v { Ok(x) | Err(x) => x } defines one "variable" x for which there are two "bindings" that will initialize the "variable"It seems like the term is derived from concepts in algebra that aren't really relevant to imperative programming languages
The concepts of free and bound variables are relevant to imperative programming languages, it's just that in basically all compiled/checked languages, _all_ variables are bound.
I believe that technically, binding is binding the name to the variable, not a value to variable. I.e., let x; is a variable binding, x = 42; is not.
To extend eddyb's example: one name, one variable, two bindings: Ok(x) | Err(x) => x, one name, two variables, two bindings: let x; let x; two names, two variables, two bindings: let x; let y.
@nrc Yeah, although it was getting complex enough to also add the distinctions between "initializing through pattern binding" and "initialization through assignment", which seem unique to Rust.
All of this makes sense, but when we're talking about how the book should explain it, how much does this terminology help users understand the language? How often do users need to talk about Ok(x) and Err(x) as "bindings" instead of just talking about the match statement?
@withoutboats I find "bindings" to be mostly useful when talking about patterns _specifically_.
And even then, I don't think I would use the word "binding" at all for a newcomer, seems unnecessary.
_As long as_ they get the constant-variable / immutable-mutable orthogonality, they should be good.
but when we're talking about how the book should explain it, how much does this terminology help users understand the language?
I hope it is rare that you have to use really precise terminology. But what I think is important is avoiding the wrong terminology. We might choose to only use variable or only use binding or whatever, but we should be very careful to avoid using binding to mean variable (or whatever) so that users are not confused when they google a term or if they are familiar with from another language. That is why I think it is important to have a precise understanding of the terms, even if they are only rarely used in the book.
It was verbose forming multiple questions, so I'm asserting... Please correct me if I'm wrong.
let x: u32; //a variable of type u32 is created and bound to the symbolic name x
x = 1; //the value 1 is assigned to the variable x
//technically, the assignment operator is applied to x with a parameter of 1
is equivalent to: let x: u32 = 1; and let x = 1u32;.
Therefore let (x, y) = (1u32, 2u32); _conceptually_ means:
not:
Of course, they optimise to the same thing, but I think it's an important distinction.
let (x, y) = (1u32, 2u32);
let (a, b) = (x, y);
Because a variable is created in every variable binding: variable binding == variable declaration. The fact that temporaries are optimised away is orthogonal.
Because a variable is created in every variable binding: variable binding == variable declaration. The fact that temporaries are optimised away is orthogonal.
This, of course, only holds in irrefutable patterns. OTOH, each pattern in a match arm's alternation (i.e., pat | pat | ... => expr) must bind the same variables, which are declared once (by the arm).
I hope it is rare that you have to use really precise terminology. But what I think is important is avoiding the wrong terminology. We might choose to only use variable or only use binding or whatever, but we should be very careful to avoid using binding to mean variable (or whatever) so that users are not confused when they google a term or if they are familiar with from another language. That is why I think it is important to have a precise understanding of the terms, even if they are only rarely used in the book.
I believe we're trying to say "variable binding" throughout. If anyone finds anyplace that the book is incorrect, I have some heart emojis for a pull request!! 鉂わ笍 鉂わ笍 鉂わ笍 鉂わ笍 鉂わ笍 鉂わ笍 鉂わ笍
@carols10cents Only when referring to the syntactical leaf of a pattern?
(Even then, it doesn't seem _that_ friendly to newcomers, and, really, do we _need_ to be that specific?)
@carols10cents Only when referring to the syntactical leaf of a pattern?
(Even then, it doesn't seem that friendly to newcomers, and, really, do we need to be that specific?)
I have no idea what is right or wrong anymore, which is why I'd love if someone could fix any spots that are wrong :)
@carols10cents I think the thrust of this conversation is that the way the book uses 'variable bindings' may be more accessible if it just used the term 'variable' instead. The term 'binding' seems uncommon in the documentation of popular languages, whereas 'variable' is fairly ubiquitous. Some people do like to use "binding" for the pattern in which a variable is created, but we don't think the book needs to get that specific.
What do you think? Do you think the book would be clearer if it just called them "variables"?
I've created a pull request so we can review and discuss with context.
Another detail that came up in #281 has to do with assignments, and I think we could use:
mutI've taken a look at the current text in the book and the comments on this thread, and agree with @eddyb's comment, as does @nikomatsakis.
In particular:
let x; // declares a variable x, which is not yet bound
let x = 37; // declares a variable x, which is bound to the value 37
const x: i32 = 37; // declares a constant x, whose value is 37
In this usage, "variable" and "constant" both refer to _names_, but distinguish whether those names will be bound to run-time or compile-time values, respectively. A _binding_ is what attaches one of these names to a value. This is all very standard terminology across a wide range of programming languages, including those that have a similar binding/mutation story to Rust (e.g. Scala).
Note that it is the _variable_ not the _binding_, which is mutable: after all, let mut x is valid, and declares that the variable x is mutable, despite no binding yet being present. It is possible to understand variable mutation as a kind of re-binding.
I agree with the general thrust of this thread that the book will be smoother if we use standard terminology and don't highlight the terminology as much.
@eddyb
Another detail that came up in #281 has to do with assignments, ...
Hm, it sounds like you're proposing to say that let x = 37 is assigning, rather than binding? While dealing with mutable variables is tricky, I don't feel great about forgoing the "binding" terminology here.
@aturon Well, with let it _looks_ a bit ambiguous because there's a = there, but I'd categorize it under the more general "initialization through pattern binding", assignments being lvalue = rvalue instead.
What's important though, is that x = y; is _always_ an "assignment", but let x = 5; and let x; x = 5; are both "initializing x with 5", whereas let mut x = 5; x = 6; is "mutates x from 5 to 6".
That is, assignment doesn't imply mutation, which I believe matches the difference between other uses of, e.g. "initial assignment" and "reassignment" in English.
Even if we forgo the "binding" terminology in most of the book, I think it's important to explain it somewhere, as it's used commonly enough in the ecosystem that a new rust user is bound (no pun intended) to come across it.
I've added a section defining _variable binding_. After another pass, I think using binding as a verb is easy enough to understand and in certain cases like patterns is clearer than the alternatives.
I think using bindings as a noun (or in place of variable) was the major comprehension issue here. I've changed some instances in accordance with @eddyb's suggestions. Thanks everyone for helping me understand this precisely.
Closed by merging in #281 and adding b386875. Thank you everyone!!
I like using _bind_ and _bound_, but not before explaining what they mean. I added a section explaining them, but removed it and all uses of _bind_ and _bound_ after @nrc said:
I don't see any benefit from introducing the 'binding' concept here - I'd just say we "declare the variable and initialise it with a value"
If we use terminology without explaining it, we limit the book's comprehension to those who already know programming languages where the same terminology is used. This is especially bad for users new to programming, or those who've used something like javascript without understanding the technical details.
The case could be made that anyone interested in programming has a certain level of intelligence and thus can infer the meanings of _bind_ and _bound_ from the context. But what about non-native English speakers? And is it reasonable to expect the percentage who read the physical book and are obsessed with precise understanding to go searching for an explanation?
This could be solved with an _appendix_ and a reference to that, but I would argue that for maximum readability, the terms shouldn't be used before they're defined and the explanation should be inline in the appropriate place.
The alternative is only using them in the _Patterns_ chapter, along with an explanation of _bindings_ in the context of _pattern leaves_, as suggested by @eddyb.
I don't think "bind" and "bound" are programming terminology. They have a meaning that is common outside of programming:
- a. To tie or secure, as with a rope or cord.
It's the same thing that's happening here, we tie a name together with a value.
Most helpful comment
I disagree. The consistent & orthogonally useful terminology I prefer to use is:
const(orstaticwhen interior mutability doesn't apply)&STATIC as *const _ as usizestatic(global) or bindings in patterns (local)match v { Ok(x) | Err(x) => x }defines one "variable"xfor which there are two "bindings" that will initialize the "variable"