[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
>lookups.
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
ahead.
>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