[Python-Dev] Switch statement

Phillip J. Eby pje at telecommunity.com
Thu Jun 22 15:36:23 CEST 2006

At 01:08 PM 6/22/2006 +0200, M.-A. Lemburg wrote:
>Phillip J. Eby wrote:
> > Maybe the real answer is to have a "const" declaration, not necessarily 
> the
> > way that Fredrik suggested, but a way to pre-declare constants e.g.:
> >
> >      const FOO = 27
> >
> > And then require case expressions to be either literals or constants.  The
> > constants need not be computable at compile time, just runtime.  If a
> > constant is defined using a foldable expression (e.g. FOO = 27 + 43), then
> > the compiler can always optimize it down to a code level
> > constant.  Otherwise, it can just put constants into cells that the
> > functions use as part of their closure.  (For that matter, the switch
> > statement jump tables, if any, can be put in a cell too.)
> >
> >> I don't like "first use" because it seems to invite tricks.
> >
> > Okay, then I think we need a way to declare a global as being 
> constant.  It
> > seems like all the big problems with switch/case basically amount to us
> > trying to wiggle around the need to explicitly declare constants.
>I don't think that this would help us much:
>If you want the compiler to "see" that a name binds to a constant,
>it would need to have access to the actual value at compile time
>(e.g. code object definition time).

No, it wouldn't.  This hypothetical "const" would be a *statement*, 
executed like any other statement.  It binds a name to a value -- and 
produces an error if the value changes.  The compiler doesn't need to know 
what it evaluates to at runtime; that's what LOAD_NAME or LOAD_DEREF are 
for.  ;)

>However, it is common practice to put constants which you'd use
>in e.g. parsers into a separate module and you certainly don't
>want to have the compiler import the module and apply attribute

Not necessary, but I see it does produce a different problem.

>This means that you'd have to declare a symbol constant in the
>scope where you want to use it as such. Which would result in
>long sections of e.g.
>const case1
>const case2
>const caseN

Actually, under my proposal it'd be:

const FOO = somemodule.FOO
const BAR = somemodule.BAR

etc.  Which is probably actually worse.  But I see your point.

>In the end, making this implicit in the case part of the switch
>statement would save us a lot of typing.
>However, there's another catch: if we do allow arbitrary expressions
>in the case parts we still need to evaluate them at some point:
>a. If we do so at compile time, the results may be a lot different
>than at execution time (e.g. say you use time.time() in one of the
>case value expressions).

We can't do that at compile time.

>b. If we evaluate them at code object execution time (e.g. module
>import), then we'd run into similar problems, but at least
>the compiler wouldn't have to have access to the used symbols.
>c. If we evaluate at first-use time, results of the evaluation
>become unpredictable and you'd also lose a lot of the
>speedup since building the hash table would consume cycles
>that you'd rather spend on doing other things.

Assuming that a sequential search takes 1/2N equality tests on average, 
you'll come out ahead by the third switch executions, assuming that the 
time to add a dictionary entry or do a hash lookup is roughly equal to an 
if/else test.  The first execution would put N entries in the dictionary, 
and do 1 lookup.  The second execution does 1 lookup, so we're now at N+2 
operations, vs N operations on average for sequential search.  At the third 
execution, we're at N+3 vs. 2.5N, so for more than 6 entries we're already 

>d. Ideally, you'd want to create the hash table at compile time
>and this is only possible using literals or by telling the
>compiler to regard a specific set of globals as constant, e.g.
>by passing a dictionary (mapping globals to values) to compile().

I still think that it suffices to assume that an expression produced using 
only symbols that aren't rebound are sufficiently static for use in a case 
expression.  If a symbol is bound by a single import statement (or other 
definition), or isn't bound at all (e.g. it's a builtin), it's easy enough 
to assume that it's going to remain the same.

Combine that compile-time restriction with a first-use build of the 
dictionary, and I think you have the best that we can hope to do in 
balancing implementation simplicity with usefulness and 
non-confusingness.  If it's not good enough, it's not good enough, but I 
don't think there's anything we've thought of so far that comes out with a 
better set of tradeoffs.

More information about the Python-Dev mailing list