[Python-Dev] Switch statement (original) (raw)

Raymond Hettinger rhettinger at ewtllc.com
Mon Jun 19 21:30:28 CEST 2006


> But there is a definite readability improvement in that you know > that it's always the same variable that is being compared and that no > other conditions are snuck into some branches.

Hmm, when I saw that "arbitrary expressions" were being proposed, I took that took mean that the variable would have to be repeated in the branches: switch x: case x.endswith('wart'): salicylicacid() case x.endswith('roid'): preparationh() default: chickensoup() That seems insane, since then it would be just different syntax for if/elif. The example looks deceptive: surely the 'switch' expression should allow an arbitrary expression, so the 'case' wouldn't be able to refer to the switch part by a name unless there was syntax (or a convention) for defining a name by which it could be referenced. I think Perl 6 is defining a very general "matching" syntax which people interested in this might want to study, just to see how far one can stretch the insanity.

I share that view 100%. Can we conclude that arbitrary expressions are fine for the switch value but that the case values must be constants?
That would neatly dispense with some proposed hypergeneralizations and keep the discussion focused.

Given:

switch x: case 1: one() case 2: two() case 3: three() default: toomany() Do we require that x be hashable so that the compiler can use a lookup table? That's a good question. We could define switch/case in terms of a hash table created by the compiler, and then raising an exception if x is unhashable is fair game.

+1

Or we could define it in terms of successive '==' comparisons, and then the compiler would have to create code for a slow path in case x is unhashable.

Too perilous. I would not like to put us in a position of generating duplicate code or funky new opcodes for the case suites. Also, it is better for the user to know that hash is going to be called, that the default-clause will execute when the key in not found, and that a KeyError would be raised if x is unhashable. This is simple, explainable, consistent behavior. Besides, if we've agreed that the case values are required to be constants, then there isn't much in the way of use cases for x being unhashable.

I don't think I'm in favor of always taking the default path when x is unhashable; that would cause some surprises if an object defines eq to be equal to ints (say) but not hash.

That would be unpleasant.

Note that we currently don't have a strong test for hashable; it's basically "if hash(x) doesn't raise an exception" which means that we would have to catch this exception (or perhaps only TypeError) in order to implement the slow path for the successive-comparisons semantics. I note that C doesn't require any particular implementation for switch/case; there's no rule that says the numbers must fit in an array of pointers or anything like that. So I would be careful before we define this in terms of hash tables. OTOH the hash table semantics don't require us to commit to a definition of hashable, which is an advantage. How's that for a wishy-washy answer. :-) Perfect. Wishy-washy answers reflect an open mind and they contain the seeds of complete agreement.

My thought is that we should define switching in terms of hash tables. It builds off of existing knowledge and therefore has a near zero learning curve. The implementation is straight-forward and there are none of the hidden surprises that we would have with fastpath/slowpath approaches which use different underlying magic methods and do not guarantee order of execution.

If use cases eventually emerge for an alternative path using successive == comparisons, then it can always be considered and added later. For now, YAGNI (neither the functionality, nor the implementation headaches, nor the complexity of explaining what it does under all the various cases).

Raymond



More information about the Python-Dev mailing list