3 ms·
At that point everything is "a change to global interpreter state", even a locally created object allocates memory from a global allocator. So clearly this dist
by dvdkon 3y ago
At that point everything is "a change to global interpreter state", even a locally created object allocates memory from a global allocator. So clearly this distinction as you present it doesn't matter.
What matters is language (not interpreter) semantics, here it's about variable scoping. In C, if I include "a.h" and then "b.h", "b.h" will have access to definitions from "a.h", even though lexically they were never brought into scope in that file. This is an actual problem that C/C++ developers have to deal with, since it makes it hard to create isolated headers.
In Python, the code in each module is evaluated in an isolated context, and returned as an object, so this isn't a problem. Edge cases like monkey patching don't really matter for >99% of usecases.
- thaumasiotes 3y ago> At that point everything is "a change to global interpreter state", even a locally created object allocates memory from a global allocator. Why is that a change to the interpreter state? Consider some more C: { int a = 1; a = 2; a = 3; } The line declaring `a` makes a change to the interpreter state; `a` becomes a reference to something. Without line 1, lines 2 and 3 are invalid. The two lines after that, manipulating the value of `a`, don't make any such change. If you delete line 2, line 3 will be handled in exactly the same way as otherwise. The reassignments to `a` make changes to the program's runtime state, but they have no effect on the interpreter. The braces, which I guess I have to call lines 0 and 4, are the opposite - they're very relevant to the interpreter, but it's hard to give them a runtime interpretation.
- dvdkon 3y agoYou have a very specific idea of what an "interpreter" is that I don't think matches the standard usage. The debate would be better served if we spoke in more concrete terms such as local/global scope, parser, analyser, AST or runtime library.
- thaumasiotes 3y agoI've taken the word from the comment I responded to. It appears to refer to the part of the process that is responsible for determining what a line of code written by the programmer is supposed to do. The particular task of resolving references isn't necessarily handled by anything you mentioned - it is the job of the parser to determine that a reference is being made, but not whether that reference goes anywhere. Similarly, this information is not included in the abstract syntax tree, which is fundamentally the same thing as the parser. It's perfectly possible to form a syntax tree that calls for the invocation of a function that doesn't exist, or assignment to a variable that doesn't exist. In C writing an invocation of a function that doesn't exist will give you a compiler error, but in Python it won't; you'll get working code that trusts that whenever it happens to be invoked, the function will exist. And if, at invocation time, it doesn't, you'll get a runtime error instead. Since this is the problem being addressed by the "interpreter", it appears to be the case that the interpreter in C exists at a different point of the compilation-execution pipeline than the interpreter in Python. The logical steps of determining what a program does need not coincide with the physical steps that are taken to convert the program into an executable artifact - or more fastidiously, they don't need to occur in the same order from program to program, and might be bundled with different other steps. But what I've claimed is that the import facility, whatever it might be, is responsible for making changes to the state of the interpreter, wherever the interpreter might be active. It makes the difference between "the programmer has specified doing something meaningless" and "the programmer has specified doing something meaningful", and the meaning that it provides gets loaded into the state of the interpreter. It is not strictly necessary that this occur at the top level of the program, the global scope, but it is almost always the case because that is the scope where programmers almost always want imported code to be available to them. > In C, if I include "a.h" and then "b.h", "b.h" will have access to definitions from "a.h", even though lexically they were never brought into scope in that file. This actually appears to be the opposite of the normal concern with lexical scope. Lexical scope allows names defined at a higher scope to be accessed from a lower scope, but that's precisely what you're trying to prevent here. It's worth noting that b.c is compiled without any knowledge of a.h or a.c (assuming it doesn't itself #include a.h, which would make the problem you mention something intentional instead of a pitfall). "libb" doesn't suffer any problems from being #included after "liba". The problem occurs in your code, where you have access to definitions from both header files, which is what you wanted when you #included both of them.