[Python-3000] Ruminations on switch, constants, imports, etc.

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

-- Talin

More information about the Python-3000 mailing list