[Python-3000] Ruminations on switch, constants, imports, etc.
talin at acm.org
Mon Sep 18 15:38:35 CEST 2006
"Ruminating" is the best word I can think of here - I've been slowly
digesting the ideas and discussions over the last couple months. Part of
the reason why all this is relevant to me is that I'm working on a
couple of side projects, some of which involve "mini-languages" that
have similar issues to what has been discussed on the list.
Bear in mind that none of what I say here is a recommendation of where
Python *should* go - rather, it's a description of where I have
*already* gone in some of the work that I am doing. It is merely one
possible answer out of many to the suggestions that have been put
forward in this forum.
(And if this is merely a rehash of something that was discussed long
ago, I apologize.)
I'll start with the 'switch' discussion (you'll note that the ideas here
cut across a bunch of different threads.) The controversy, for which
there seemed to be no final resolution, had to do with when the case
values should be evaluated (I won't repeat the descriptions of the
various schools - look in the PEP.)
As some people pointed out, the truly fundamental issue has to do with
the nature of constants in Python, which is to say that currently, there
are none - other than literals.
What would be entailed in adding a const type? Without fundamentally
changing the language, the best you can do is early binding, in other
words the const value isn't known at compilation time, but is a variable
that is frozen at some point - perhaps at module load time.
Adding a true const - that is, a variable whose value is known to the
compiler, and can be optimized as such - is somewhat more involved. For
one thing, the compiler knows nothing about external modules, or
anything outside the current compilation unit. Without the ability to
import external definitions, a compile-time 'const' is quite useless.
One way around this (which is a little kludgey) would be to add a second
type of 'import' - a compile-time import, which could be called
something like 'include' or 'using'.
An import of this type would act as if it had been textually included in
the current source file. It would become part of the current compilation
unit, and it would have the same restrictions - such as the inability to
access variables imported via 'import' at compile time.
Include files can of course include other files - but they can also
'import' as well. The effect of the importing from an include is the
same as importing from the primary source file (because of the rule
which states that 'include' is equivalent to textual inclusion.)
Conversely, imported files can include - however the effect of the
inclusion is limited to the imported file only, and does not affect the
primary source file (because it's a different compilation unit.)
This implies that you can't access constants that are within an imported
module (because the constant definitions exist only within the compiler
- they are transformed into literals before code generation occurs.)
If a source file and an included module need to share constant values,
they must each include the definitions of those constants. This can lead
to problems if the include files are changing - one file might be
compiled with a different version of the include file than another.
OTOH, many potential uses for constants would be for things like
operating system error codes and flags, which are fairly stable and
unchanging -- so even a restricted use of the facility (i.e. don't use
it for values which are in flux) might be worth while. Another
possibility is to embed include checksums or other version info within
the compiled file.
So far, it seems like a lot of added complexity for fairly little
benefit. However, where it gets interesting is when you realize that
once you've given the compiler knowledge of the world outside a single
compilation unit, a number of interesting possibilities arise.
The one I've been experimenting with in my mini-language is macros. Not
macros in the C sense, but in the lisp sense - a function which takes
unevaluated arguments. Actually, they more closely resemble Dylan
macros, in that they add production rules to the parser. Internally a
macro is a small snippet of an AST, which gets spliced into the AST of
the calling function at compile time. Macro arguments can be
identifiers, expressions, and statements, all of which get substituted
into the appropriate point in the AST.
This is of course going way past Guido's "Programmable Syntax"
prohibition. Good thing I am only talking hypothetically!
It seems to me, however, (getting back to 'switch') that supporting a
proper 'switch' statement has to address these issues in *some* fashion
- the issue of constants isn't going to go away completely under any of
the proposed approaches.
In fact, I'm actually leaning towards the position of *not* adding a
switch statement to Python, simply because I'm not sure that Python
*should* deal with all of these issues. It seems to me that adding
'const' to the language opens up a Pandora's box, containing both chaos
and hope - and I think that for some language X, it may be a good idea
to open that box, but I don't know if X includes Python.
More information about the Python-3000