From 3e857976b3e2b2d4821cc4ac6bd582cd12fa9890 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Mon, 26 Jun 2006 18:05:39 +0000 Subject: [PATCH] Add PEP 3103: A Switch/Case Statement (GvR). Also fix a reference to cvs in the Makefile. --- Makefile | 2 +- pep-0000.txt | 2 + pep-3103.txt | 126 +++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 111 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index 7289bbe3f..ed29ad995 100644 --- a/Makefile +++ b/Makefile @@ -23,4 +23,4 @@ clean: -rm *.html update: - cvs update -P -d + svn update diff --git a/pep-0000.txt b/pep-0000.txt index 873cb3043..1d92aee8f 100644 --- a/pep-0000.txt +++ b/pep-0000.txt @@ -99,6 +99,7 @@ Index by Category S 754 IEEE 754 Floating Point Special Values Warnes S 3101 Advanced String Formatting Talin S 3102 Keyword-Only Arguments Talin + S 3103 A Switch/Case Statement GvR Finished PEPs (done, implemented in Subversion) @@ -431,6 +432,7 @@ Numerical Index I 3100 Python 3.0 Plans Kuchling, Cannon S 3101 Advanced String Formatting Talin S 3102 Keyword-Only Arguments Talin + S 3103 A Switch/Case Statement GvR Key diff --git a/pep-3103.txt b/pep-3103.txt index abb0c33f1..c67310439 100644 --- a/pep-3103.txt +++ b/pep-3103.txt @@ -8,7 +8,7 @@ Type: Standards Track Python-Version: 3.0 Content-Type: text/x-rst Created: 25-Jun-2006 -Post-History: never +Post-History: 26-Jun-2006 Abstract @@ -24,6 +24,11 @@ This PEP should be seen as an alternative to PEP 275. My views are somewhat different from that PEP's author, but I'm grateful for the work done in that PEP. +This PEP introduces canonical names for the many variants that have +been discussed for different aspects of the syntax and semantics, such +as "alternative 2", "school II", "Option 3" and so on. Hopefully +these names will help the discussion. + Rationale ========= @@ -216,10 +221,10 @@ like this:: case [*]EXPR, [*]EXPR, ...: -Note that the * notation is similar to the use of prefix * already in -use for variable-length parameter lists and for passing computed -argument lists, and often proposed for value-unpacking (e.g. "a, b, -*c = X" as an alternative to "(a, b), c = X[:2], X[2:]"). +The * notation is similar to the use of prefix * already in use for +variable-length parameter lists and for passing computed argument +lists, and often proposed for value-unpacking (e.g. "a, b, *c = X" as +an alternative to "(a, b), c = X[:2], X[2:]"). Alternative D ------------- @@ -390,16 +395,17 @@ otherwise an if/elif chain would have little chance of success either. Assuming the switch will be executed many times, doing some extra work the first time pays back quickly by very quick dispatch times later. -A mostly theoretical objection to this option is that there is no -obvious object where the dispatch dict can be stored. It can't be -stored on the code object, which is supposed to be immutable; it can't -be stored on the function object, since many function objects may be -created for the same function (e.g. for nested functions). In -practice, I'm sure that something can be found; it could be stored in -a section of the code object that's not considered when comparing two -code objects or when pickling or marshalling a code object; or all -switches could be stored in a dict indexed by weak references to code -objects. +An objection to this option is that there is no obvious object where +the dispatch dict can be stored. It can't be stored on the code +object, which is supposed to be immutable; it can't be stored on the +function object, since many function objects may be created for the +same function (e.g. for nested functions). In practice, I'm sure that +something can be found; it could be stored in a section of the code +object that's not considered when comparing two code objects or when +pickling or marshalling a code object; or all switches could be stored +in a dict indexed by weak references to code objects. The solution +should also be careful not to leak switch dicts between multiple +interpreters. Another objection is that the first-use rule allows obfuscated code like this:: @@ -419,17 +425,101 @@ equivalent to this:: but that's not what it does (unless it is always called with the same value as the second argument). This has been addressed by suggesting that the case expressions should not be allowed to reference local -variables. But this is somewhat arbitrary. +variables, but this is somewhat arbitrary. A final objection is that in a multi-threaded application, the first-use rule requires intricate locking in order to guarantee the correct semantics. (The first-use rule suggests a promise that side -effects of case expressions are incurred exactly once.) +effects of case expressions are incurred exactly once.) This may be +as tricky as the import lock has proved to be, since the lock has to +be held while all the case expressions are being evaluated. Option 3 '''''''' -TBD +A proposal that has been winning support (including mine) is to freeze +a switch's dict when the innermost function containing it is defined. +The switch dict is stored on the function object, just as parameter +defaults are, and in fact the case expressions are evaluated at the +same time and in the same scope as the parameter defaults (i.e. in the +scope containing the function definition). + +This option has the advantage of avoiding many of the finesses needed +to make option 2 work: there's no need for locking, no worry about +immutable code objects or multiple interpreters. It also provides a +clear explanation for why locals can't be referenced in case +expressions. + +This option works just as well for situations where one would +typically use a switch; case expressions involving imported or global +named constants work exactly the same way as in option 2, as long as +they are imported or defined before the function definition is +encountered. + +A downside however is that the dispatch dict for a switch inside a +nested function must be recomputed each time the nested function is +defined. For certain "functional" styles of programming this may make +switch unattractive in nested functions. (Unless all case expressions +are compile-time constants; then the compiler is of course free to +optimize away the swich freezing code and make the dispatch table part +of the code object.) + +Another downside is that under this option, there's no clear moment +when the dispatch dict is frozen for a switch that doesn't occur +inside a function. There are a few pragmatic choices for how to treat +a switch outside a function: + + (a) Disallow it. + (b) Translate it into an if/elif chain. + (c) Allow only compile-time constant expressions. + (d) Compute the dispatch dict each time the switch is reached. + (e) Like (b) but tests that all expressions evaluated are hashable. + +Of these, (a) seems too restrictive: it's uniformly worse than (c); +and (d) has poor performance for little or no benefits compared to +(b). It doesn't make sense to have a performance-critical inner loop +at the module level, as all local variable references are slow there; +hence (b) is my (weak) favorite. Perhaps I should favor (e), which +attempts to prevent atypical use of a switch; examples that work +interactively but not in a function are annoying. In the end I don't +think this issue is all that important (except it must be resolved +somehow) and am willing to leave it up to whoever ends up implementing +it. + +When a switch occurs in a class but not in a function, we can freeze +the dispatch dict at the same time the temporary function object +representing the class body is created. This means the case +expressions can reference module globals but not class variables. +Alternatively, if we choose (b) above, we could choose this +implementation inside a class definition as well. + +Option 4 +'''''''' + +There are a number of proposals to add a construct to the language +that makes the concept of a value pre-computed at function definition +time generally available, without tying it either to parameter default +values or case expressions. Some keywords proposed include 'const', +'static', 'only' or 'cached'. The associated syntax and semantics +vary. + +These proposals are out of scope for this PEP, except to suggest that +*if* such a proposal is accepted, there are two ways for the switch to +benefit: we could require case expressions to be either compile-time +constants or pre-computed values; or we could make pre-computed values +the default (and only) evaluation mode for case expressions. The +latter would be my preference, since I don't see a use for more +dynamic case expressions that isn't addressed adequately by writing an +explicit if/elif chain. + + +Conclusion +========== + +It is too early to decide. I'd like to see at least one completed +proposal for pre-computed values before deciding. In the mean time, +Python is fine without a switch statement, and perhaps those who claim +it would be a mistake to add one are right. Copyright