Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Are 'variable bindings' valuable? #276

Closed
elahn opened this issue Oct 2, 2016 · 27 comments
Closed

Are 'variable bindings' valuable? #276

elahn opened this issue Oct 2, 2016 · 27 comments
Assignees

Comments

@elahn
Copy link
Contributor

elahn commented Oct 2, 2016

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:

  • a core concept
  • a way of explaining/thinking about what's happening
  • an implementation detail

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:

  • A variable implies changeability/mutability, so we need a different concept to make immutability clear.
  • A value/reference is a separate concept from the symbolic name we use to represent it.
  • A separate concept is needed to explain why an immutable variable can contain a mutable reference.
  • To make it clear that moving a variable binding does not necessarily mean copying its memory.
  • To make it clear that declaring a binding does not necessarily mean copying or allocating memory.

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?

@carols10cents
Copy link
Member

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!

@skade
Copy link
Contributor

skade commented Oct 2, 2016

I prefer speaking about bindings. There's other important things bound by introducing a variable.

  • The inferred or specified type
  • lifetimes and drops

(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".

@withoutboats
Copy link

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.

@Veedrac
Copy link

Veedrac commented Oct 3, 2016

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.

@withoutboats
Copy link

withoutboats commented Oct 3, 2016

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.

It seems like the term is derived from concepts in algebra that aren't really relevant to imperative programming languages

@eddyb
Copy link
Member

eddyb commented Oct 3, 2016

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:

  • "value": an instance of a type, an element of the "value set" of a type, etc.
  • "constant": definition which is associated with a single "value" for the whole duration of the program, introduced by const (or static when interior mutability doesn't apply)
    • "linker constant": a "constant" for which the value is not determined at compile-time before linking, which can be dynamic linking (at program load time), e.g.: &STATIC as *const _ as usize
  • "variable": definition which may be associated different "values" at different points in time, introduced by static (global) or bindings in patterns (local)
    • "binding a variable": associating the definition with a "value"
    • "variable binding": syntactical leaf of a pattern that results in the "value" at that position matched by the pattern being bound to the "variable", e.g.: match v { Ok(x) | Err(x) => x } defines one "variable" x for which there are two "bindings" that will initialize the "variable"
    • "mutable variable": a "variable" which can also be "mutated" (i.e. "assigned") after "binding" the initial "value" (i.e. "initialization")

@nrc
Copy link
Member

nrc commented Oct 3, 2016

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.

@eddyb
Copy link
Member

eddyb commented Oct 3, 2016

@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.

@withoutboats
Copy link

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?

@eddyb
Copy link
Member

eddyb commented Oct 3, 2016

@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.

@nrc
Copy link
Member

nrc commented Oct 3, 2016

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.

@elahn
Copy link
Contributor Author

elahn commented Oct 3, 2016

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:

  • x and y are bound to new variables of type u32, 1 is assigned to x, 2 is assigned to y

not:

  • x is bound to the value 1 of type u32, y is bound to the value 2 of type u32

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);
  • a and b are bound to new variables of type u32, x is assigned to a, y is assigned to b
  • x and y are moved, so no copying is necessary, but this is conceptually orthogonal

Because a variable is created in every variable binding: variable binding == variable declaration. The fact that temporaries are optimised away is orthogonal.

@eddyb
Copy link
Member

eddyb commented Oct 3, 2016

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).

@carols10cents
Copy link
Member

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!! ❤️ ❤️ ❤️ ❤️ ❤️ ❤️ ❤️

@eddyb
Copy link
Member

eddyb commented Oct 3, 2016

@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
Copy link
Member

@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 :)

@withoutboats
Copy link

withoutboats commented Oct 4, 2016

@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"?

@elahn
Copy link
Contributor Author

elahn commented Oct 4, 2016

I've created a pull request so we can review and discuss with context.

@eddyb
Copy link
Member

eddyb commented Oct 4, 2016

Another detail that came up in #281 has to do with assignments, and I think we could use:

  • "assignment": syntactical construct that results in an association of a variable with a value, can be:
    • "initialization": gives the variable its initial value, which is needed to read the variable
      • can also be done through bindings in a pattern, when matching a value against that pattern
    • "mutation": gives the variable a new, potentially different value
      • only allowed if the variable was declared with mut

@aturon
Copy link
Member

aturon commented Oct 4, 2016

I'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.

@aturon
Copy link
Member

aturon commented Oct 4, 2016

@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.

@eddyb
Copy link
Member

eddyb commented Oct 4, 2016

@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.

@elahn
Copy link
Contributor Author

elahn commented Oct 4, 2016

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.

@elahn
Copy link
Contributor Author

elahn commented Oct 5, 2016

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.

@carols10cents
Copy link
Member

Closed by merging in #281 and adding b386875. Thank you everyone!!

@elahn
Copy link
Contributor Author

elahn commented Oct 9, 2016

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.

@carols10cents
Copy link
Member

I don't think "bind" and "bound" are programming terminology. They have a meaning that is common outside of programming:

  1. 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants