[Python-Dev] Switch statement
Phillip J. Eby
pje at telecommunity.com
Thu Jun 22 20:37:20 CEST 2006
I think one of the problems I sometimes have in communicating with you is
that I think out stuff from top to bottom of an email, and sometimes
discard working assumptions once they're no longer needed. We then end up
having arguments over ideas I already discarded, because you find the
problems with them faster than I do, and you assume that those problems
carry through to the end of my message. :) So, I'm partially reversing
the order of my reply, so you can see what I'm actually proposing, before
the minutiae of responding the objections you raised to stuff I threw out
either in my previous message or the message before that. Hopefully this
will help.
At 10:44 AM 6/22/2006 -0700, Guido van Rossum wrote:
>Please, think through the notion of const declarations more before
>posting again. Without const declarations none of this can work
Actually, the "const" declaration part isn't necessary and I already
discarded the idea in my previous reply to you, noting that the
combination of these facets can be made to work without any explicit const
declarations:
1. "case (literal|NAME)" is the syntax for equality testing -- you can't
use an arbitrary expression, not even a dotted name.
2. NAME, if used, must be bound at most once in its defining scope
3. Dictionary optimization can occur only for literals and names not bound
in the local scope, others must use if-then.
This doesn't require explicit "const" declarations at all. It does,
however, prohibit using "import A" and then switching on a bunch of "A.foo"
values. You have to "from A import foo, bar, baz" instead.
If you like this, then you may not need to read the rest of this message,
because most of your remaining comments and questions were based on an
assumption that "const" declarations were necessary.
>On 6/22/06, Phillip J. Eby <pje at telecommunity.com> wrote:
> > At 09:37 AM 6/22/2006 -0700, Guido van Rossum wrote:
> > >On 6/22/06, Phillip J. Eby <pje at telecommunity.com> wrote:
> > > > 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. ;)
> > >
> > >Please think this through more. How do you implement the "produces an
> > >error if the value changes" part? Is the const property you're
> > >thinking of part of the name or of the object it refers to?
> > >
> > >The only way I can see it work is if const-ness is a compile-time
> > >property of names, just like global. But that requires too much
> > >repetition when a constant is imported.
> >
> > Right; MAL pointed that out in the message I was replying to, and I
> > conceded his point. Of course, if you consider constness to be an implicit
> > property of imported names that aren't rebound, the repetition problem goes
> > away.
>
>Um, technically names are never imported, only objects. Suppose module
>A defines const X = 1, and module B imports A. How does the compiler
>know that A.X is a constant?
It doesn't. You have to "from A import X". At that point, you have a name
that is bound by an import that can be considered constant as long as the
name isn't rebound later.
> > And if you then require all "case" expressions to be either literals or
> > constant names, we can also duck the "when does the expression get
> > evaluated?" question. The obvious answer is that it's evaluated wherever
> > you bound the name, and the compiler can either optimize the switch
> > statement (or not), depending on where the assignment took place.
>
>I don't understand what you're proposing. In particular I don't
>understand what you mean by "wherever you bound the name".
>
>So (evading the import problem for a moment) suppose we have
>
>const T = int(time.time())
>
>def foo(x):
> switch x:
> case T: print "Yes"
> else: print "No"
>
>Do you consider that an optimizable switch or not?
Yes. What I'm trying to do is separate "when the dictionary is
constructed" from "when the expression is evaluated". If we restrict the
names used to names that have at most one binding in their defining scope,
then we can simply add the dictionary entries whenever the *name is
bound*. Ergo, the evaluation time is apparent from simple reading of the
source - we are never moving the evaluation, only determining how early we
can add information to the switching dictionary.
Thus, the answer to "when is the expression evaluated" is "when it's
executed as seen in the source code". There is thus no magic of either
first-use or function-definition involved. What you see is exactly what
you get.
> > A switch
> > that's in a loop or a function call can only be optimized if all its
> > constants are declared outside the loop or function body; otherwise it
> > degrades to an if/elif chain.
>
>What do you mean by a switch in a function call? Syntactically that
>makes no sense. Do you mean in a function definition?
Yes, sorry. I probably copied the slip from your previous post. ;)
>I think you're trying to give a heuristic here for when it's likely
>that the switch will be executed multiple times with all cases having
>the same values (so the optimized dict can be reused).
Actually, no, I'm trying to say that we should restrict case expressions to
either literals or symbolic constants, trying to give a rigorous definition
for "symbolic constant", and trying to specify what portions of a switch
statement can be implemented using a dictionary lookup, as distinguished
from those parts that require if/elif tests.
>I'm afraid this is going to cause problems with predictability.
Actually, it seems quite clear to me. Any "case NAME:" where NAME is bound
in local scope must be implemented internally using "if" instead of a
dictionary lookup. Any "case literal:" or "case NAME:" where NAME is *not*
bound in the local scope can be optimized. (Because that means NAME is
bound in an outer scope and can be assumed constant in the current scope.)
>Also,
>if it degenerates to an if/elif chain, does it still require the
>switch expression to be hashable?
Yes, if there are any "case literal:" branches, or "case NAME:" where the
name isn't bound in the local scope.
More information about the Python-Dev
mailing list