[Python-Dev] First Draft: PEP "Switching on Multiple Values"
Skip Montanaro
skip@pobox.com (Skip Montanaro)
Sun, 11 Nov 2001 12:41:58 +0100
Just a few odd comments...
mal> The current solution to this problem lies in using a dispatch
mal> table to find the case implementing method to execute depending on
mal> the value of the switch variable (this can be tuned to have a
mal> complexity of O(1) on average, e.g. by using perfect hash
mal> tables). This works well for state machines which require complex
mal> and lengthy processing in the different case methods. It does
should be "does not"
mal> perform well for ones which only process one or two instructions
mal> per case, e.g.
...
mal> The first solution has the benefit of not relying on new keywords
either "not requiring new" or "not relying on adding new"
mal> to the language, while the second looks cleaner. Both involve some
mal> run-time overhead to assure that the switching variable is
mal> immutable and hashable.
...
mal> The new optimization should not change the current Python
mal> semantics (by reducing the number of __cmp__ calls and adding
mal> __hash__ calls in if-elif-else constructs which are affected
mal> by the optimiztation). To assure this, switching can only
mal> safely be implemented either if a "from __future__" style
mal> flag is used, or the switching variable is one of the builtin
mal> immutable types: int, float, string, unicode, etc.
and not an instance of a subclass of any of them. (I think that's implied
by your text, but should be explicitly stated.)
mal> To prevent post-modifications of the jump-table dictionary
mal> (which could be used to reach protected code), the jump-table
mal> will have to be a read-only type (e.g. a read-only
mal> dictionary).
There was discussion once upon a time about adding a read-write flag to
dictionaries to prevent modification during sensitive times. I gather lists
already have such a flag to allow in-place sorting to work.
...
mal> It should be possible for the compiler to detect an
mal> if-elif-else construct which has the following signature:
mal> if x == 'first':...
mal> elif x == 'second':...
mal> else:...
mal> (ie. the left hand side alwys references the same variable,
mal> the right hand side some hashable immutable builtin type)
I don't think it is required that the right-hand sides all be of the same
type. Perhaps it would be worthwhile to mention this and illustrate with an
example. (Odd usage, perhaps, but still agrees with the desired constrains
I think.)
mal> The compiler could then setup a read-only (perfect) hash
mal> table, store it in the constants and add an opcode SWITCH
mal> which triggers the following run-time behaviour:
I think it would be sufficient to use a read-only dictionary. Perfect has
tables are fine, but who wants to implement one?
mal> At runtime, SWITCH would check x for being one of the
mal> well-known immutable types (strings, unicode, numbers) and
mal> use the hash table for finding the right opcode snippet.
And if one of the constraints isn't met, it would simply jump to the
original if/elif/else code?
...
mal> The compiler would have to generate code similar to this:
should be "would have to compile" or "would have to convert"
...
mal> Issues:
mal> There have been other proposals for the syntax which reuse
mal> existing keywords and avoid adding two new ones ("switch" and
mal> "case").
Here's a wacky idea (probably wouldn't work syntactically, but hey, you
never know):
if EXPR:
== CONSTANT:
suite
== CONSTANT:
suite
== CONSTANT:
suite
else:
suite
No new keywords, but I suspect the compiler would have to look too far ahead
to realise that it's not compiling a regular if statement.
BTW, do you mean for your "suite"s to be optional? Here are some
concrete examples using each syntax variant
switch EXPR: switch x:
case CONSTANT: case "first":
[suite] print x
case CONSTANT: case "second":
[suite] x = x**2
... print x
else: else:
[suite] print "whoops!"
case EXPR: case x:
of CONSTANT: of "first":
[suite] print x
of CONSTANT: of "second":
[suite] print x**2
else: else:
[suite] print "whoops!"
case EXPR: case state:
if CONSTANT: if "first":
[suite] state = "second"
if CONSTANT: if "second":
[suite] state = "third"
else: else:
[suite] state = "first"
when EXPR: when state:
in CONSTANT_TUPLE: in ("first", "second"):
[suite] print state
in CONSTANT_TUPLE: state = next_state(state)
[suite] in ("seventh",):
... print "done"
else: break # out of loop!
[suite] else:
print "middle state"
state = next_state(state)
If you allow the suites to be optional (except in the else?), I think
you can avoid the need for tuples without parens in the case clauses.
For example:
when state:
in "first":
in "second":
print state
state = next_state(state)
in "seventh":
print "done"
break
else:
print "middle state"
state = next_state(state)
Skip