[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