There are so many replies that I am going to try and summarize responses with a lot of cut and paste in one post. Sorry if this is the wrong way to do it.
Do you have some concrete Python code which would clearly be improved by this proposal?
Let me explain myself. I am a low-level programmer fascinated by Python's elegant syntax and how it is executed. We actually do little Python programming here but we do allow interaction between Python and our product and so I am not able to show any concrete code. I guess that makes me a crank but I am fine with that. At a low level, I look at what Python has to go through to execute statements and thoughts swirl through my mind as to how it could be improved. I finally cracked and made a post here with one of those improvements.
const ST_MODE = 0
So, the compiler will ?replace any use of the identifier with? the constant value.
Yes, the compiler will replace any use of the name with its value. A statement like:
If c == ST_MODE:
Would be treated by the compiler at compile-time as if it had seen:
If c == 0:
The name ST_MODE in this example is not a bindable name. The name only lives during compilation and is not accessible at run-time. It would not be stored in a dictionary (unless the magic syntax 'require module as *' were used that only confuses what I am trying to say).
name_prefix = "ST_"
foo = globals().get(name_prefix + "MODE")
What do you expect the compiler to do in the above code?
Since the name is not accessible at run-time, the above would produce an exception. Const names are only available at compile-time.
If I'd written his proposal I'd have probably termed these things "bind-once", generating names that may not be rebound. They would still
need to be carefully placed if the compiler were to have the option of constant folding i.e. they're need to be outside function and class
definitions, determinable from static analysis.
These are "bind-never" names. The compiler would have to be able to see the definition when a module that uses them is being compiled. That is the reason for the require statement. The compiler does not normally look at the contents of other modules when parsing a source file. The require statement tells it to do so.
The expression would be restricted to result in an immutable object
What is the purpose of this restriction?
My thought is that a constant name should have the same value regardless of context. If I were to say something like "const A = B" then A is no longer a constant and when substituted depends on how B is interpreted within the current context (is it a global? A local? A nonlocal?). If I were to say "const A = [1,2,3]" then you need to worry about side effects. You would have to entirely clone the value at compile-time for each use rather than simply incrementing the reference count.
Is that the driving use-case for your suggestion? Compile-time efficiency?
If so, then I suspect that you're on the wrong track. As I understand it,
the sort of optimizations that PyPy can perform at runtime are far more
valuable than this sort of constant substitution.
That is my basic track. If the existing tools handle this better than my idea should be discarded as not providing any significant improvement and adding additional baggage to the language.
k = ("Some value", "Another value") # for example
x = k
y = k
assert x is y # this always passes, no matter the value of k
But if k is a const, it will fail, because the lines "x = k" and "y = k"
will be expanded at compile time:
The restriction that constant names be immutable objects would allow their values to be placed in the constant pool for the function. In the above, if 'k' were a constant name then it would (hopefully) reside in a single location in the constant pool and the assignments to 'x' and 'y' would access the same constant pool location.
Another question: what happens if the constant expression can't be
evaluated until runtime?
Constant expressions would be restricted to be compile-time constants. They would not be evaluated at run-time.
Compiler-enforced immutability is one of those really hard problems which,
if you manage to do flexibly and correctly, would be an academically
publishable result, not something you hack into the interpreter over a
I have to plead guilty here. I am not an academic and do not know all the implications of things. I do not follow research either and so am basically proposing this as a crank/hacker sort of person.
- multi-stage computations, so the program is partially-evaluated at
"compile" time and the `const` sections computed. This is also really hard.
Furthermore, if you want to be able to use bits of the standard library in
the early stages (you probably do, e.g. for things like min, max, len,
etc.) either you'd need to manually start annotating huge chunks of the
standard library to be available at "compile" time (a huge undertaking) or
you'll need an effect-tracking-system to do it for you.
This is indeed a big worry. I would have had it such that a module could (but not required in any sense) be split into two parts. One part that is referenced at run-time using the existing import mechanism. This would not change. The second part of a module would be constants (and only constants) that are referenced at compile-time. There would be no requirement that modules change over to this new method. It would just mean that constants defined in the module are available to the compiler. That last statement is apparently not exactly true if I understand the comments about what PyPy optimizations do.
The idea of the compiler accessing the source files for other modules does give me pause. Currently, compiling one module is fairly disjoint from other modules in that a change to one module does not require a re-compile of modules that use it even if 'constants' are changed. This is a good feature of Python and maybe something to boast about but I would worry if these ideas introduced bad practices. I don't think that there have been many cases of Python programmers saying: "I made a change to my module - you need to recompile your module to get the changes".
I would like to thank you all for critiquing my ideas and pointing out its flaws with patience and respect. In many ways this was just an exercise in getting things off my chest because in the end I am just a crank.
Harding, James wrote:
The name ST_MODE in this example is not a bindable name. The name only lives during compilation and is not accessible at run-time.
I don't think that's a good idea. It would be better for it to be available at run time like a normal module-level name, but protected from rebinding.
There may be cases where the compiler can't work out the value, such as when the module is imported dynamically. Such code would then continue to work, it just wouldn't be optimised.
Not having the name present at run time could also lead to unexpected results. If something tries to rebind the name, it will succeed, but it won't affect compiler-optimised code using the name. It would be better if attempting to rebind a const name raised an exception.