Interactions between variable hoisting and other aspects of a programming language
Asked Answered
E

1

3

The following Python program A outputs 1, as expected, while the following Python program B raises an unbound local variable x error, counterintuitively.

  • Program A:
def f(): print(x)
x = 1
f()
  • Program B:
def f(): print(x); x = 2
x = 1
f()

Javascript has the exact same behaviour.

  • Program A:
function f() { console.log(x); }
let x = 1;
f();
  • Program B:
function f() { console.log(x); let x = 2; }
let x = 1;
f();

However, C++ outputs 1 in both cases, as expected.

  • Program A:
#include <iostream>
int x;
void f() { std::cout << x; }
int main() { x = 1; f(); return 0; }
  • Program B:
#include <iostream>
int x;
void f() { std::cout << x; int x = 2; }
int main() { x = 1; f(); return 0; }

So all programs A output 1. The differences in programs B between Python and Javascript on the one hand, and C++ on the other hand, result from their different scoping rules: in C++, the scope of a variable starts at its declaration, while in Python and Javascript, it starts at the beginning of the block where the variable is declared. Consequently, in C++ printing variable x in function f resolves to the value 1 of global variable x since it is the only variable in context at this point of execution. In Python and Javascript printing variable x in function f resolves to nothing and raises an unbound local variable x error since local variable x is already in context at this point of execution and therefore it masks global variable x without being bound yet to the value 2. This counterintuitive behaviour of Python and Javascript is also known as variable hoisting since it ‘hoists’ variable declarations (but not definitions) at the beginning of their blocks.

Presumably variable hoisting interacts with other programming language design decisions, which leads to some languages choosing to use it and others not to. What are those interactions?

Entourage answered 10/8, 2020 at 9:7 Comment(4)
"counterintuitive behaviour" is due to the block/function scope. Under the hood, declarations are faster to find from the top of their scope, when resolving variables in nested scopes. Originally in JS (interpreted, var and function scope only), you could run "stupid" code without breaking the execution. The drawback was hard debuggable logic errors.Sorosis
@Sorosis Yes var does not break execution like let does since it assigns the value undefined to local variable x instead of nothing, but the scope of local variable x remains the same since it still masks global variable x like let does.Cute
Yep, that's a consequence of hoisting. In the context of let (and const), a term temporal dead-zone is used.Sorosis
Guido van Rossum (creator of Python) himself answered here.Cute
B
1

This is more a artifact of the language, rather than a programmer oriented feature.

For python and javascript, the new variable means allocate an entry in the name dictionary, until you actually create an object and assign it. In C++ there is no name dictionary in the run-time, the definition needs to actually allocate memory for the object (and it could be a 10MB array for all we know).

This does allow C++ to fit into smaller memory footprint if you really need that. Otherwise there's no much reason to think about it.

From the developer's perspective you have a bug. Your x has 2 meanings. Programming is hard enough as it is without having variables change meaning on you, so I would avoid as much as possible. I think C++ would give you a warning in some such a cases.

In practice you would get used to either setup.

Bohunk answered 10/8, 2020 at 10:19 Comment(4)
Interesting. So are you saying that variable hoisting is a desirable programming feature because it prevents name binding to both a non local entity and a local entity in the same block? No, neither Clang nor GCC give a warning for the C++ programs.Cute
#25152024Bohunk
@Maggyero I would prefer a clear Warning or Error to tell you this is happening. The errors you get in your hoisting examples can be misinterpreted and worked around.Bohunk
I asked the question about the motivation for variable hoisting on the official Python forum and finally Guido himself gave the rationale. If you are interested, the post is here.Cute

© 2022 - 2024 — McMap. All rights reserved.