Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Hi, On Mon, Jun 26, 2006 at 12:23:00PM -0700, Guido van Rossum wrote: Feedback (also about misrepresentation of alternatives I don't favor) is most welcome, either to me directly or as a followup to this post. So my 2 cents, particularly about when things are computed and ways to control that explicitly: there was a point in time where I could say that I liked Python because language design was not constrained by performance issues. Looks like it's getting a matter of the past, small step by small step. I'll have to get used to mentally filter out 'static' or whatever the keyword will be, liberally sprinkled in programs I read to make them slightly faster. Maybe I should, more constructively, propose to start a thread on the subject of: what would be required to achieve similar effects as the intended one at the implementation level, without strange early-computation semantics? I'm not talking about Psyco stuff here; there are way to do this with reasonably-simple refactorings of global variable accesses. I have experimented a couple of years ago with making them more direct (just like a lot of people did, about the faster LOAD_GLOBAL trend). I dropped this as it didn't make things much faster, but it had a nice side-effect: allowing call-backs for binding changes. This would be a good base on top of which to make transparent, recomputed-when-changed constant-folding of simple expressions. Building dicts for switch and keeping them up-to-date... Does it make sense for me to continue this discussion? A bientot, Armin. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Greg Ewing wrote: Nick Coghlan wrote: By 'current namespace' I really do mean locals() - the cell objects themselves would be local variables from the point of view of the currently executing code. This is wrong. Cells are *parameters* implicitly passed in by the calling function. They may temporarily be referenced from the current scope, but their home has to be in an outer scope, otherwise they won't survive between calls. As far as I'm aware, the cell objects get kept alive by the references to them from the closure attribute of the inner function. The actual execution frame of the outer function still goes away. The cell values persist because the function object persists between calls - it's only the execution frame that gets reinitialised every time. However, I'm now clearer on the fact that Guido's main interest is in true once-per-process semantics for case expressions, which changes the design goals I was working towards. So I think I'll try to take a break from this discussion, and let ideas percolate in the back of my head for a while :) Cheers, Nick. -- Nick Coghlan | [EMAIL PROTECTED] | Brisbane, Australia --- http://www.boredomandlaziness.org ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Eric Sumner wrote: Forget subroutines for a moment - the main point of the thread was the idea that the dispatch table was built explicitly rather than automatically - that instead of arguing over first-use vs. function-definition, we let the user decide. I'm sure that my specific proposal isn't the only way that this could be done. But anything that makes the build explicit is going to be so much more ugly. And I still think you're trying to solve the wrong problem. Only if the programmer has to see it. The dispatch table need not include the behaviors of each of the cases; it only needs to define what the cases are. In most of the use cases I've seen, switch is used to define behavior for different values of an enumeration. The dispatch table for an enumeration can be built wherever the values for the enumeration are defined (such as in a module). Programmers don't need to bother with making a dispatch table unless they are defining enumeration values themselves. You mean something like this?: switch x in colours: case RED: # whatever case GREEN: # whatever case BLUE: # whatever I think Guido's right. It doesn't solve the underlying problem because the compiler still has to figure out how to build a dispatch table from the possible values in colours to the actual bytecode offsets of the cases. Cheers, Nick. -- Nick Coghlan | [EMAIL PROTECTED] | Brisbane, Australia --- http://www.boredomandlaziness.org ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
On 6/29/06, Nick Coghlan [EMAIL PROTECTED] wrote: You mean something like this?: switch x in colours: case RED: # whatever case GREEN: # whatever case BLUE: # whatever I think Guido's right. It doesn't solve the underlying problem because the compiler still has to figure out how to build a dispatch table from the possible values in colours to the actual bytecode offsets of the cases. To implement this, you actually need two lookup tables: one particular to the switch that maps labels to bytecode offsets, and one in the dispatch table to map values to labels. The former is built when the switch is compiled, and the latter is built wherever the dispatch table is defined. Each lookup is still O(1), so the whole operation remains O(1). It is O(n) or worse to check that all of the cases in the switch are defined in the dispatch table, but that only has to be done once per dispatch table/switch statement pair, and can then be stred in one or the other (probably the dispatch table, as that will be a proper object). -- Eric Sumner ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Eric Sumner wrote: You mean something like this?: switch x in colours: case RED: # whatever case GREEN: # whatever case BLUE: # whatever I think Guido's right. It doesn't solve the underlying problem because the compiler still has to figure out how to build a dispatch table from the possible values in colours to the actual bytecode offsets of the cases. To implement this, you actually need two lookup tables: one particular to the switch that maps labels to bytecode offsets, and one in the dispatch table to map values to labels. The former is built when the switch is compiled, and the latter is built wherever the dispatch table is defined. Each lookup is still O(1), so the whole operation remains O(1). what's a label ? /F ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
You mean something like this?: switch x in colours: case RED: # whatever case GREEN: # whatever case BLUE: # whatever I think Guido's right. It doesn't solve the underlying problem because the compiler still has to figure out how to build a dispatch table from the possible values in colours to the actual bytecode offsets of the cases. To implement this, you actually need two lookup tables: one particular to the switch that maps labels to bytecode offsets, and one in the dispatch table to map values to labels. The former is built when the switch is compiled, and the latter is built wherever the dispatch table is defined. Each lookup is still O(1), so the whole operation remains O(1). what's a label ? In your example, RED, GREEN, and BLUE. colours provides a mapping from values to labels/cases, and the switch statement provides a mapping from labels/cases to code. Sorry about introducing a new term without saying anything about it. -- Eric Sumner ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Eric Sumner wrote: what's a label ? In your example, RED, GREEN, and BLUE. colours provides a mapping from values to labels/cases, and the switch statement provides a mapping from labels/cases to code. Sorry about introducing a new term without saying anything about it. yeah, but what are they? integers? strings? names without an associated value? how do you create new labels? where are they stored? who keeps track of them? /F ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
yeah, but what are they? integers? strings? names without an associated value? how do you create new labels? where are they stored? who keeps track of them? In this scheme, dispatch tables can be considered to be reverse-lookup namespaces. Where a regular namespace is used to look up a value given its name, a dispatch table is used to look up a name given its value. The switch statement then lets you actually do something based on which name is returned. -- Eric Sumner ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Talin [EMAIL PROTECTED] wrote: My version of this is to add to Python the notion of a simple old-fashioned subroutine - that is, a function with no arguments and no additional scope, which can be referred to by name. For example: I don't like the idea of an embedded subrutine for a few reasons. One of them is because you need to define the case - sub mapping dictionaries in each pass, you are getting no improvement in speed (which is a motivating factor in this discussion). Even worse, the disconnect between case definition and dispatch makes it feel quite a bit like a modified label/goto proposal. The ultimate killer is that your proposed syntax (even using def) make this construct less readable than pretty much any if/elif/else chain I have ever seen. - Josiah ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Guido van Rossum wrote: I think we all agree that side effects of case expressions is one way how we can deduce the compiler's behind-the-scenes tricks (even School Ib is okay with this). So I don't accept this as proof that Option 2 is better. OK, I worked out a side effect free example of why I don't like option 3: def outer(cases=None): def inner(option, force_default=False): if cases is not None and not force_default: switch option: case in cases[0]: # case 0 handling case in cases[1]: # case 1 handling case in cases[2]: # case 2 handling # Default handling return inner I believe it's reasonable to expect this to work fine - the case expressions don't refer to any local variables, and the subscript operations on the closure variable are protected by a sanity check to ensure that variable isn't None. There certainly isn't anything in the code above to suggest to a reader that the condition attempting to guard evaluation of the switch statement might not do its job. With first-time-execution jump table evaluation, there's no problem - when the closure variable is None, there's no way to enter the body of the if statement, so the switch statement is never executed and the case expressions are never evaluated. Such functions will still be storing a cell object for the switch's jump table, but it will always be empty because the code to populate it never gets a chance to run. With the out of order execution involved in def-time evaluation, however, the case expressions would always be executed, even though the inner function is trying to protect them with a sanity check on the value of the closure variable. Using Option 3 semantics would mean that calling outer() given the above function definition will give you the rather surprising result TypeError: 'NoneType' object is unsubscriptable, with a traceback pointing to the line case cases[0]: in the body of a function that hasn't been called, and that includes an if statement preventing that line from being reached when 'cases' is None. When it comes to the question of where do we store the result? for the first-execution calculation of the jump table, my proposal is a hidden cell in the current namespace. Um, what do you mean by the current namespace? You can't mean the locals of the function containing the switch. There aren't always outer functions so I must conclude you mean the module globals. But I've never seen those referred to as the current namespace. By 'current namespace' I really do mean locals() - the cell objects themselves would be local variables from the point of view of the currently executing code. For functions, the cell objects would be created at function definition time, for code handled via exec-style execution, they'd be created just before execution of the first statement begins. In either case, the cell objects would already be in locals() before any bytecode gets executed. It's only the calculation of the cell *contents* that gets deferred until first execution of the switch statement. So do I understand that the switch gets re-initialized whenever a new function object is created? That seems a violation of the first time executed rule, or at least a modification (first time executed per defined function). Or am I misunderstanding? I took it as a given that 'first time execution' had to be per function and/or invocation of exec - tying caching of expressions that rely on module globals or closure variables to code objects doesn't make any sense, because the code object may have different globals and/or closure variables next time it gets executed. I may not have explained my opinion about that very well though, because the alternative didn't even seem to be an option. But if I have a code object c containing a switch statement (not inside a def) with a side effect in one of its cases, the side effect is activated each time through the following loop, IIUC: d = {} for i in range(10): exec c in d Yep. For module and class level code, the caching really only has any speed benefit if the switch statement is inside a loop. The rationale for doing it that way becomes clearer if you consider what would happen if you created a new dictionary each time through the loop: for i in range(10): d = {} exec c in d print d[result] I'm confused how you can first argue that tying things to the function definition is one of the main drawbacks of Option 3, and then proceed to tie Option 2 to the function definition as well. This sounds like by far the most convoluted specification I have seen so far. I hope I'm misunderstanding what you mean by namespace. It's not the link to function definitions that I object to in Option 3, it's the idea of evaluating the cases at function
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Nick Coghlan wrote: There certainly isn't anything in the code above to suggest to a reader that the condition attempting to guard evaluation of the switch statement might not do its job. that's why the evaluation model used in the case statement needs to be explicit. that applies to the once but not really approach, as well as the static = in global scope approach (http://online.effbot.org/2006_06_01_archive.htm#pep-static). there are no shortcuts here, if we want things to be easy to explain and easy to internalize. /F ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Guido van Rossum wrote: Is it unacceptable - or impractical - to break the addition of switch to python in two (minor version separated) steps ? But what's the point? We have until Python 3000 anyway. except that we may want to reserve the necessary keywords in 2.6... /F ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Looks like this doesn't help at all when pre-computing the dispatch dict based on named constants. So this is a no-go. I should add that ABC had such named subroutines (but not for switching); I dropped them to simplify things. They're not an intrinsically undesirable or even unnecessary thing IMO. But it doesn't solve my use case for switching. The syntax is also seriously cumbersome compared to a PEP-3103-style switch. --Guido On 6/27/06, Talin [EMAIL PROTECTED] wrote: This parallels some of my thinking -- that we ought to somehow make the dict-building aspect of the switch statement explicit (which is better than implicit, as we all have been taught.) My version of this is to add to Python the notion of a simple old-fashioned subroutine - that is, a function with no arguments and no additional scope, which can be referred to by name. For example: def MyFunc( x ): sub case_1: ... sub case_2: ... sub case_3: ... # A direct call to the subroutine: do case_1 # An indirect call y = case_2 do y # A dispatch through a dict d = dict( a=case_1, b=case_2, c_case_3 ) do d[ 'a' ] -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
On 6/28/06, Nick Coghlan [EMAIL PROTECTED] wrote: Guido van Rossum wrote: I think we all agree that side effects of case expressions is one way how we can deduce the compiler's behind-the-scenes tricks (even School Ib is okay with this). So I don't accept this as proof that Option 2 is better. OK, I worked out a side effect free example of why I don't like option 3: def outer(cases=None): def inner(option, force_default=False): if cases is not None and not force_default: switch option: case in cases[0]: # case 0 handling case in cases[1]: # case 1 handling case in cases[2]: # case 2 handling # Default handling return inner I believe it's reasonable to expect this to work fine - the case expressions don't refer to any local variables, and the subscript operations on the closure variable are protected by a sanity check to ensure that variable isn't None. It's only reasonable if you're in school I. As I have repeatedly said, the only use cases I care about are those where the case expressions are constants for the lifetime of the process. (The compiler doesn't need to know this but the programmer does.) There certainly isn't anything in the code above to suggest to a reader that the condition attempting to guard evaluation of the switch statement might not do its job. With first-time-execution jump table evaluation, there's no problem - when the closure variable is None, there's no way to enter the body of the if statement, so the switch statement is never executed and the case expressions are never evaluated. Such functions will still be storing a cell object for the switch's jump table, but it will always be empty because the code to populate it never gets a chance to run. With the out of order execution involved in def-time evaluation, however, the case expressions would always be executed, even though the inner function is trying to protect them with a sanity check on the value of the closure variable. Using Option 3 semantics would mean that calling outer() given the above function definition will give you the rather surprising result TypeError: 'NoneType' object is unsubscriptable, with a traceback pointing to the line case cases[0]: in the body of a function that hasn't been called, and that includes an if statement preventing that line from being reached when 'cases' is None. That's a perfectly reasonable outcome to me. When it comes to the question of where do we store the result? for the first-execution calculation of the jump table, my proposal is a hidden cell in the current namespace. Um, what do you mean by the current namespace? You can't mean the locals of the function containing the switch. There aren't always outer functions so I must conclude you mean the module globals. But I've never seen those referred to as the current namespace. By 'current namespace' I really do mean locals() - the cell objects themselves would be local variables from the point of view of the currently executing code. For functions, the cell objects would be created at function definition time, for code handled via exec-style execution, they'd be created just before execution of the first statement begins. In either case, the cell objects would already be in locals() before any bytecode gets executed. It's only the calculation of the cell *contents* that gets deferred until first execution of the switch statement. So do I understand that the switch gets re-initialized whenever a new function object is created? That seems a violation of the first time executed rule, or at least a modification (first time executed per defined function). Or am I misunderstanding? I took it as a given that 'first time execution' had to be per function and/or invocation of exec - tying caching of expressions that rely on module globals or closure variables to code objects doesn't make any sense, because the code object may have different globals and/or closure variables next time it gets executed. I may not have explained my opinion about that very well though, because the alternative didn't even seem to be an option. PEP 3103 discusses several ways to implement first-time-really. I suggest that you edit the PEP to add option 2a which is first-time-per-function-definition. But if I have a code object c containing a switch statement (not inside a def) with a side effect in one of its cases, the side effect is activated each time through the following loop, IIUC: d = {} for i in range(10): exec c in d Yep. For module and class level code, the caching really only has any speed benefit if the switch statement is inside a loop. The rationale for doing it that way becomes clearer if you consider what would happen if you created a new
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Talin [EMAIL PROTECTED] wrote: Josiah Carlson wrote: Talin [EMAIL PROTECTED] wrote: My version of this is to add to Python the notion of a simple old-fashioned subroutine - that is, a function with no arguments and no additional scope, which can be referred to by name. For example: I don't like the idea of an embedded subrutine for a few reasons. One of them is because you need to define the case - sub mapping dictionaries in each pass, you are getting no improvement in speed (which is a motivating factor in this discussion). Even worse, the disconnect between case definition and dispatch makes it feel quite a bit like a modified label/goto proposal. The ultimate killer is that your proposed syntax (even using def) make this construct less readable than pretty much any if/elif/else chain I have ever seen. - Josiah The case - sub mapping doesn't need to be defined every time - that's the point, you as the programmer decide when and how to construct the dictionary, rather than the language trying to guess what it is you want. EIBTI. Beautiful is better than ugly. You could also define the switch in an outer function that contains an inner function that is called multiple times: def Outer(): sub S1: ... sub S2: ... sub S3: ... dispatch = { parser.IDENT: S1, parser.NUMBER: S2, parser.COMMENT: S3 } def Inner( x ): do dispatch[ x ] return Inner This allows direct access to a namespace that was previously read-only from other namespaces (right now closure namespaces are read-only, objects within them may not be). ... There is also the possibility of building the dict before the function is run, however this requires a method of peeking into the function body and extracting the definitions there. For example, suppose the subroutine names were also attributes of the function object: def MyFunc( x ): sub upper: ... sub lower: ... sub control: ... sub digit: ... do dispatch[ x ] # Lets use an array this time, for variety dispatch = [ MyFunc.upper, MyFunc.lower, MyFunc.upper, # Yes, 2 and 3 are the same as 0 and 1 MyFunc.lower, MyFunc.control, MyFunc.digit, ] ... One of my other desires for switch/case or its equivalent is that of encapsulation. Offering such access from outside or inside the function violates what Python has currently defined as its mode of operations for encapsulation. With regards to your second and third points: sure, I freely admit that this proposal is less readable than a switch statement. The question is, however, is it more readable than what we have *now*? As I have explained, comparing it to if/elif/else chains is unfair, because they don't have equivalent performance. The real question is, is it more readable than, say, a dictionary of references to individual functions; and I think that there are a number of possible use cases where the answer would be 'yes'. Why is the comparison against if/elif/else unfair, regardless of speed? We've been comparing switch/case to if/elif/else from a speed perspective certainly, stating that it must be faster (hopefully O(1) rather than O(n)), but that hasn't been the only discussion. In fact, one of the reasons we are considering switch/case is because readability still counts, and people coming from C/etc., are familliar with it. Some find switch/case significantly easier to read, I don't, but I also don't find it significantly harder to read. On the other hand, if I found someone using sub in a bit of Python code, I'd probably cry, then rewrite the thing using if/elif/else. If I was fiesty, I'd probably do some branch counting and reorder the tests, but I would never use subs. I think that language features should just work in all cases, or at least all cases that are reasonable. I don't like the idea of a switch statement that is hedged around with unintuitive exceptions and strange corner cases. And I don't like the idea of making my code ugly. I would honestly rather have no change than to have sub/def+do. - Josiah ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Let's just drop the switchable subroutine proposal. It's not viable. On 6/28/06, Josiah Carlson [EMAIL PROTECTED] wrote: Talin [EMAIL PROTECTED] wrote: Josiah Carlson wrote: Talin [EMAIL PROTECTED] wrote: My version of this is to add to Python the notion of a simple old-fashioned subroutine - that is, a function with no arguments and no additional scope, which can be referred to by name. For example: I don't like the idea of an embedded subrutine for a few reasons. One of them is because you need to define the case - sub mapping dictionaries in each pass, you are getting no improvement in speed (which is a motivating factor in this discussion). Even worse, the disconnect between case definition and dispatch makes it feel quite a bit like a modified label/goto proposal. The ultimate killer is that your proposed syntax (even using def) make this construct less readable than pretty much any if/elif/else chain I have ever seen. - Josiah The case - sub mapping doesn't need to be defined every time - that's the point, you as the programmer decide when and how to construct the dictionary, rather than the language trying to guess what it is you want. EIBTI. Beautiful is better than ugly. You could also define the switch in an outer function that contains an inner function that is called multiple times: def Outer(): sub S1: ... sub S2: ... sub S3: ... dispatch = { parser.IDENT: S1, parser.NUMBER: S2, parser.COMMENT: S3 } def Inner( x ): do dispatch[ x ] return Inner This allows direct access to a namespace that was previously read-only from other namespaces (right now closure namespaces are read-only, objects within them may not be). ... There is also the possibility of building the dict before the function is run, however this requires a method of peeking into the function body and extracting the definitions there. For example, suppose the subroutine names were also attributes of the function object: def MyFunc( x ): sub upper: ... sub lower: ... sub control: ... sub digit: ... do dispatch[ x ] # Lets use an array this time, for variety dispatch = [ MyFunc.upper, MyFunc.lower, MyFunc.upper, # Yes, 2 and 3 are the same as 0 and 1 MyFunc.lower, MyFunc.control, MyFunc.digit, ] ... One of my other desires for switch/case or its equivalent is that of encapsulation. Offering such access from outside or inside the function violates what Python has currently defined as its mode of operations for encapsulation. With regards to your second and third points: sure, I freely admit that this proposal is less readable than a switch statement. The question is, however, is it more readable than what we have *now*? As I have explained, comparing it to if/elif/else chains is unfair, because they don't have equivalent performance. The real question is, is it more readable than, say, a dictionary of references to individual functions; and I think that there are a number of possible use cases where the answer would be 'yes'. Why is the comparison against if/elif/else unfair, regardless of speed? We've been comparing switch/case to if/elif/else from a speed perspective certainly, stating that it must be faster (hopefully O(1) rather than O(n)), but that hasn't been the only discussion. In fact, one of the reasons we are considering switch/case is because readability still counts, and people coming from C/etc., are familliar with it. Some find switch/case significantly easier to read, I don't, but I also don't find it significantly harder to read. On the other hand, if I found someone using sub in a bit of Python code, I'd probably cry, then rewrite the thing using if/elif/else. If I was fiesty, I'd probably do some branch counting and reorder the tests, but I would never use subs. I think that language features should just work in all cases, or at least all cases that are reasonable. I don't like the idea of a switch statement that is hedged around with unintuitive exceptions and strange corner cases. And I don't like the idea of making my code ugly. I would honestly rather have no change than to have sub/def+do. - Josiah ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/guido%40python.org -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Guido van Rossum wrote: Let's just drop the switchable subroutine proposal. It's not viable. Perhaps not - but at the same time, when discussing new language features, let's not just limit ourselves to what other languages have done already. Forget subroutines for a moment - the main point of the thread was the idea that the dispatch table was built explicitly rather than automatically - that instead of arguing over first-use vs. function-definition, we let the user decide. I'm sure that my specific proposal isn't the only way that this could be done. -- Talin ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
On 6/28/06, Talin [EMAIL PROTECTED] wrote: Guido van Rossum wrote: Let's just drop the switchable subroutine proposal. It's not viable. Perhaps not - but at the same time, when discussing new language features, let's not just limit ourselves to what other languages have done already. Well, Python 3000 is explcitly not intended as a platform for arbitrary experimentation with feature invention (read PEP 3000). I've gotten quite a bit of mileage out of borrowing from other languages instead of inventing my own stuff, so I don't want to go out inventing as a replacement of researching options that have already been tried elsewhere. Forget subroutines for a moment - the main point of the thread was the idea that the dispatch table was built explicitly rather than automatically - that instead of arguing over first-use vs. function-definition, we let the user decide. I'm sure that my specific proposal isn't the only way that this could be done. But anything that makes the build explicit is going to be so much more ugly. And I still think you're trying to solve the wrong problem. -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Forget subroutines for a moment - the main point of the thread was the idea that the dispatch table was built explicitly rather than automatically - that instead of arguing over first-use vs. function-definition, we let the user decide. I'm sure that my specific proposal isn't the only way that this could be done. But anything that makes the build explicit is going to be so much more ugly. And I still think you're trying to solve the wrong problem. Only if the programmer has to see it. The dispatch table need not include the behaviors of each of the cases; it only needs to define what the cases are. In most of the use cases I've seen, switch is used to define behavior for different values of an enumeration. The dispatch table for an enumeration can be built wherever the values for the enumeration are defined (such as in a module). Programmers don't need to bother with making a dispatch table unless they are defining enumeration values themselves. -- Eric Sumner Note: I sent an email yesterday with a proposal to this effect, but it seems to have been lost. If anybody wants, I can resend it. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Nick Coghlan wrote: By 'current namespace' I really do mean locals() - the cell objects themselves would be local variables from the point of view of the currently executing code. This is wrong. Cells are *parameters* implicitly passed in by the calling function. They may temporarily be referenced from the current scope, but their home has to be in an outer scope, otherwise they won't survive between calls. -- Greg ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Talin wrote: The case - sub mapping doesn't need to be defined every time - that's the point, you as the programmer decide when and how to construct the dictionary, Then you seem to be proposing a variation on the constant-only case option, with a more convoluted control flow. -- Greg ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
PEP 3103, When to Freeze the Dispatch Dict/Option 1 2 things resonated with me for Raymond's proposal and the follow up: - It seemed agnostic to almost all of the independently contentious issues. - is defined tightly enough to allow room for growth and elaboration over time [Raymond]. In particular it left room for const/static/only/cached/etc to come along later. I think its worth acknowledging this in the PEP. Is nothing better than something in this case ? I don't know. I think we need a PEP for const/static/only/cached/precomputed or whatever people like to call it. Once we have (say) static, I think making the case expressions static by default would still cover all useful cases, and would allow us to diagnose duplicate cases reliably (which the if/elif chain semantics don't allow IIUC). If the expectation is that static/const will evolve as a sibling pep, does this not make Raymond's suggestion any more appealing, even a little ? Is it unacceptable - or impractical - to break the addition of switch to python in two (minor version separated) steps ? Robin ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
On 6/26/06, K.S.Sreeram [EMAIL PROTECTED] wrote: Guido van Rossum wrote: I think we need a PEP for const/static/only/cached/precomputed or whatever people like to call it. fredrik's got a micro pep at http://online.effbot.org Once we have (say) static, I think making the case expressions static by default would still cover all useful cases, and would allow us to diagnose duplicate cases reliably (which the if/elif chain semantics don't allow IIUC). Making case expressions default static would be very surprising to users because of the restrictions placed by static. For instance 'case in a', will not support containers which have a custom __contains__ method. It will also not support containers like lists, and sets because they are mutable. IMHO this doesn't feel very pythonic. Instead if we redefine the goal of the switch statement to be 'ease of expression' rather than 'optimization', then it can just be used as a concise alternative to if-elif chains, and we can make 'case in a' work with all containers where a regular 'in' statement works *AND* still give the possibility of fast lookup when the programmer wants, using explicit static. I feel programmer expressivity is more important, and default static case expressions looks like premature optimization. You've just placed yourself in School Ia (see the updated PEP). I respectfully disagree. -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
On 6/27/06, Robin Bryce [EMAIL PROTECTED] wrote: PEP 3103, When to Freeze the Dispatch Dict/Option 1 2 things resonated with me for Raymond's proposal and the follow up: - It seemed agnostic to almost all of the independently contentious issues. Except for the need to use named constants. - is defined tightly enough to allow room for growth and elaboration over time [Raymond]. In particular it left room for const/static/only/cached/etc to come along later. I think its worth acknowledging this in the PEP. Search for Raymond's name. It's there. Is nothing better than something in this case ? I don't know. I think we need a PEP for const/static/only/cached/precomputed or whatever people like to call it. Once we have (say) static, I think making the case expressions static by default would still cover all useful cases, and would allow us to diagnose duplicate cases reliably (which the if/elif chain semantics don't allow IIUC). If the expectation is that static/const will evolve as a sibling pep, does this not make Raymond's suggestion any more appealing, even a little ? No, then School Ia becomes more appealing. Raymond's proposal is unpythonic by not allowing expressions. Is it unacceptable - or impractical - to break the addition of switch to python in two (minor version separated) steps ? But what's the point? We have until Python 3000 anyway. -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
On 6/27/06, Nick Coghlan [EMAIL PROTECTED] wrote: Guido van Rossum wrote: I've written a new PEP, summarizing (my reaction to) the recent discussion on adding a switch statement. While I have my preferences, I'm trying to do various alternatives justice in the descriptions. The PEP also introduces some standard terminology that may be helpful in future discussions. I'm putting this in the Py3k series to gives us extra time to decide; it's too important to rush it. http://www.python.org/dev/peps/pep-3103/ A generally nice summary, but as one of the advocates of Option 2 when it comes to freezing the jump table, I'd like to see it given some better press :) Sure. Feel free to edit the PEP directly if you want. Feedback (also about misrepresentation of alternatives I don't favor) is most welcome, either to me directly or as a followup to this post. My preferred variant of Option 2 (calculation of the jump table on first use) disallows function locals in the switch cases just like Option 3. The rationale is that the locals can't be expected to remain the same across different invocations of the function, so caching an expression that depends on them is just as nonsensical for Option 2 as it is for Option 3 (and hence should trigger a Syntax Error either way). OK, but the explanation of Option 2 becomes more cumbersome then: instead of first time executed it now is first time executed and you cannot use any locals (but you can use locals if you're executing globally, and you can use locals of outer functions) (oh, and whether locals in a class are okay is anybody's guess). Given that variant, my reasons for preferring Option 2 over Option 3 are: - the semantics are the same at module, class and function level No they're not. At the global level, this is okay bit at the function level it's not: C = 1 switch x: case C: print 42 Unless I misunderstand you and you want to disallow locals at the global level too, in which case I see this okay at the function level but not at the global level: switch x: case re.IGNORECASE: print 42 So I don't see how this is really true. - the order of execution roughly matches the order of the source code Only roughly though. One can still create obfuscated examples. - it does not cause any surprises when switches are inside conditional logic As an example of the latter kind of surprise, consider this: def surprise(x): do_switch = False if do_switch: switch x: case sys.stderr.write(Not reachable!\n): pass Option 2 won't print anything, since the switch statement is never executed, so the jump table is never built. Option 3 (def-time calculation of the jump table), however, will print Not reachable! to stderr when the function is defined. That's a pretty crooked example if you ask me. I think we all agree that side effects of case expressions is one way how we can deduce the compiler's behind-the-scenes tricks (even School Ib is okay with this). So I don't accept this as proof that Option 2 is better. Now consider this small change, where the behaviour of Option 3 is not only surprising but outright undefined: def surprise(x): if 0: switch x: case sys.stderr.write(Not reachable!\n): pass The optimiser is allowed to throw away the contents of an if 0: block. This makes no difference for Option 2 (since it never executed the case expression in the first place), but what happens under Option 3? Is Not reachable! written to stderr or not? This is a good question. I think both behaviors are acceptable. Again, the problem is with the side-effect-full case expression, not with Option 3. When it comes to the question of where do we store the result? for the first-execution calculation of the jump table, my proposal is a hidden cell in the current namespace. Um, what do you mean by the current namespace? You can't mean the locals of the function containing the switch. There aren't always outer functions so I must conclude you mean the module globals. But I've never seen those referred to as the current namespace. The first time the switch statement is executed, the cell object is empty, so the jump table creation code is executed and the result stored in the cell. On subsequent executions of the switch statement, the jump table is retrieved directly from the cell. OK. For functions, the cell objects for any switch tables would be created internally by the function object constructor based on the attributes of the code object. So the cells would be created anew each time the function definition is executed. These would be saved on the function object and inserted into the local namespace under the appropriate names before the code is executed (this is roughly the same thing that is done for closure variables). Deleting from the namespace afterwards isn't necessary, since
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Given that variant, my reasons for preferring Option 2 over Option 3 are: - the semantics are the same at module, class and function level - the order of execution roughly matches the order of the source code - it does not cause any surprises when switches are inside conditional logic As an example of the latter kind of surprise, consider this: def surprise(x): do_switch = False if do_switch: switch x: case sys.stderr.write(Not reachable!\n): pass Option 2 won't print anything, since the switch statement is never executed, so the jump table is never built. Option 3 (def-time calculation of the jump table), however, will print Not reachable! to stderr when the function is defined. Good points on order of define vs order of execution surprises. WARNING: probable over generalization below or really usefull idea depending on your point of view. ;) I use dict base dispatching in a number of my programs and like it with the exception I need to first define all the code in functions (or use lambda) even if they are only one line. So it results in a three step process, define functions, define dict, and then call it. And I need to make sure all the function calls use the same calling signature. In some cases I'm passing variables that one function doesn't need because it is needed in one of the other cases. So modeling the switch after dictionary dispatching more directly where the switch is explicitly defined first and then used later might be good both because it offers reuse in the current scope and it can easily be used in code that currently uses dict style dispatching. switch name: 1: ... TWO: ... 'a', 'b', 'c': ... in range(5,10): ... else: ... for choice in data: do choice in name:# best calling form I can think of. I think this avoids most of the define time/order and optimization issues as well as the issues I have with dict base dispatching so I thought it might be worth a mention. There may still be some advantage to evaluating the case expressions early, but I think it might not be needed as much in this form so they could be evaluated at switch definition time, which is the order the code is written. The main arguments against this form might be that it approaches macro and named blocks capabilities a bit too closely, but those may also be arguments for it as this may more directly fulfill the reasons some want named blocks and or macros. To use a named switch in such a way just call the desired case explicitly ... switch responses: 'make_red': ... 'make_blue': ... do 'make_red' in responses: ... do 'make_red' in responses: # again ... do 'make_blue' in responses: ... So it offers local reuse of code in a more direct way than a switch statement does and more closely matches that which is current practice with dictionary dispatching. Cheers, Ron Just a thought, Ron ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
On 6/27/06, Ron Adam [EMAIL PROTECTED] wrote: I use dict base dispatching in a number of my programs and like it with the exception I need to first define all the code in functions (or use lambda) even if they are only one line. So it results in a three step process, define functions, define dict, and then call it. And I need to make sure all the function calls use the same calling signature. In some cases I'm passing variables that one function doesn't need because it is needed in one of the other cases. So modeling the switch after dictionary dispatching more directly where the switch is explicitly defined first and then used later might be good both because it offers reuse in the current scope and it can easily be used in code that currently uses dict style dispatching. switch name: 1: ... TWO: ... 'a', 'b', 'c': ... in range(5,10): ... else: ... for choice in data: do choice in name:# best calling form I can think of. It looks like your proposal is to change switch into a command that defines a function of one parameter. Instead of the do expression in switch call you could just call the switch -- no new syntax needed. Your example above would be for choice in data: name(choice) # 'name' is the switch's name However, early on in the switch discussion it was agreed that switch, like if/elif, should not create a new scope; it should just be a control flow statement sharing the surrounding scope. The switch as function definition would require the use of globals. Also, it would make sense if a switch could be a method instead of a function. I realize that by proposing a new invocation syntax (do ... in ...) you might have intended some other kind of interaction between the switch and the surrounding scope. but exactly what you're proposing isn't very clear from your examples, since you don't have any example code in the case suites, just -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Guido van Rossum wrote: On 6/27/06, Ron Adam [EMAIL PROTECTED] wrote: I use dict base dispatching in a number of my programs and like it with the exception I need to first define all the code in functions (or use lambda) even if they are only one line. So it results in a three step process, define functions, define dict, and then call it. And I need to make sure all the function calls use the same calling signature. In some cases I'm passing variables that one function doesn't need because it is needed in one of the other cases. So modeling the switch after dictionary dispatching more directly where the switch is explicitly defined first and then used later might be good both because it offers reuse in the current scope and it can easily be used in code that currently uses dict style dispatching. switch name: 1: ... TWO: ... 'a', 'b', 'c': ... in range(5,10): ... else: ... for choice in data: do choice in name:# best calling form I can think of. It looks like your proposal is to change switch into a command that defines a function of one parameter. Instead of the do expression in switch call you could just call the switch -- no new syntax needed. Your example above would be for choice in data: name(choice) # 'name' is the switch's name I thought of using a function call so it would be more like using a generator, but also ruled it out because it does create a new scope and I think closures may complicate it or it would require also passing all the names needed for each case which would get old quick if it is required every time. One of the things I want to be able to avoid in dict based dispatching for cases with only one or two lines of code. So my intent was that it use the local scope and not use the function call signature which implies a new scope to the reader and a returned value, thus the 'do choice in name' calling form. No returned value is needed because it has full access to local name space. for example you wouldn't write... return if x: 42 else: 84 but would instead... if x: y = 42 else: y = 84 return y The 'do' is used in the same context an 'if' is used. switch a: True: y=42 else: y=84 do x in a: return y However, early on in the switch discussion it was agreed that switch, like if/elif, should not create a new scope; it should just be a control flow statement sharing the surrounding scope. The switch as function definition would require the use of globals. Also, it would make sense if a switch could be a method instead of a function. There's no reason why it couldn't be put in a method. If the switch uses the surrounding name space you have that flexibility. I'm not sure if the select definition could be put in the body of a class and have the do's in a method. That would be like having an if in the body of the class and the else to it in a method, so I would think it wouldn't be allowed. So they both would need to be in the same name space and the select will always need to be defined before the 'do' is executed. I realize that by proposing a new invocation syntax (do ... in ...) you might have intended some other kind of interaction between the switch and the surrounding scope. but exactly what you're proposing isn't very clear from your examples, since you don't have any example code in the case suites, just What was intended probably would be more closely related to constructing a switch with BASICS gosub command. one: # in basic these do not have their own scope print 'one' return # return from subroutine not function here two: print 'two' return three: print 'three' return data = ('one', 'two', 'three') for choice in data: if choice == 'one': gosub one elif choice == 'two': gosub two elif choice == 'three': gosub three Which would be better expressed as.. switch choices: 'one': print 'one' 'two': print 'two' 'three': print 'three' for choice in ('one', 'two', 'three'): do choice in choices Each case label expression would be evaluated when the switch block is executed, ie... in order it appears in the program, but the code for each case would be skipped until a (do choice in choices) line. Each switch case block would not fall through but return to the next line after the 'do' line by default. The whole thing could be put in a separate function or method if it's desired to get the single function call form you suggested along with a separate name space. def switcher(choice): switcher roo: 1: a = 42 42: a = 1 else: raise ValueError do choice in switcher: return a switcher(1) - 42
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
On 6/27/06, Ron Adam [EMAIL PROTECTED] wrote: Guido van Rossum wrote: It looks like your proposal is to change switch into a command that defines a function of one parameter. Instead of the do expression in switch call you could just call the switch -- no new syntax needed. Your example above would be for choice in data: name(choice) # 'name' is the switch's name I thought of using a function call so it would be more like using a generator, but also ruled it out because it does create a new scope and I think closures may complicate it or it would require also passing all the names needed for each case which would get old quick if it is required every time. One of the things I want to be able to avoid in dict based dispatching for cases with only one or two lines of code. So my intent was that it use the local scope and not use the function call signature which implies a new scope to the reader and a returned value, thus the 'do choice in name' calling form. No returned value is needed because it has full access to local name space. for example you wouldn't write... return if x: 42 else: 84 but would instead... if x: y = 42 else: y = 84 return y The 'do' is used in the same context an 'if' is used. switch a: True: y=42 else: y=84 do x in a: return y Ah, I see. However, early on in the switch discussion it was agreed that switch, like if/elif, should not create a new scope; it should just be a control flow statement sharing the surrounding scope. The switch as function definition would require the use of globals. Also, it would make sense if a switch could be a method instead of a function. There's no reason why it couldn't be put in a method. If the switch uses the surrounding name space you have that flexibility. I'm not sure if the select definition could be put in the body of a class and have the do's in a method. That would be like having an if in the body of the class and the else to it in a method, so I would think it wouldn't be allowed. So they both would need to be in the same name space and the select will always need to be defined before the 'do' is executed. I realize that by proposing a new invocation syntax (do ... in ...) you might have intended some other kind of interaction between the switch and the surrounding scope. but exactly what you're proposing isn't very clear from your examples, since you don't have any example code in the case suites, just What was intended probably would be more closely related to constructing a switch with BASICS gosub command. I understand now. But I have a question: if I write for i in range(10): switch S: case i: print 42 (i.e. the switch is *inside* the for loop) does the switch get defined 10 times (with 10 different case values!) or not? one: # in basic these do not have their own scope print 'one' return # return from subroutine not function here two: print 'two' return three: print 'three' return data = ('one', 'two', 'three') for choice in data: if choice == 'one': gosub one elif choice == 'two': gosub two elif choice == 'three': gosub three Which would be better expressed as.. switch choices: 'one': print 'one' 'two': print 'two' 'three': print 'three' for choice in ('one', 'two', 'three'): do choice in choices I'm not sure I like the idea of using BASIC as a way to explain Python functionality... :-) Each case label expression would be evaluated when the switch block is executed, ie... in order it appears in the program, but the code for each case would be skipped until a (do choice in choices) line. Each switch case block would not fall through but return to the next line after the 'do' line by default. The whole thing could be put in a separate function or method if it's desired to get the single function call form you suggested along with a separate name space. def switcher(choice): switcher roo: 1: a = 42 42: a = 1 else: raise ValueError do choice in switcher: return a switcher(1) - 42 switcher(42) - 1 switcher(100) - raises exception I'm still unclear on when you propose the case expressions to be evaluated. Each time the switch statement is encountered? That would be the most natural given the rest of your explanation. But then a switch inside a function that references globally defined constants would be re-evalulated each time the function is called; much of the discussion here is focused on trying to reduce the number of times the switch cases are evaluated to once per program invocation or once per function *definition*. -- --Guido van Rossum (home page: http://www.python.org/~guido/)
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
But what's the point? We have until Python 3000 anyway. Ah, my mistake. In my enthusiasm, I foolishly got the time frames of peps 3103 275 mixed up. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Guido van Rossum wrote: What was intended probably would be more closely related to constructing a switch with BASICS gosub command. I understand now. But I have a question: if I write for i in range(10): switch S: case i: print 42 (i.e. the switch is *inside* the for loop) does the switch get defined 10 times (with 10 different case values!) or not? In this instance the switch would be redefined 10 times. The ending switch would be: switch S: case 10: print 42 The static keyword could be used with this form as well to force define time evaluation. (see last example.) I'm not sure I like the idea of using BASIC as a way to explain Python functionality... :-) Yes, I agree! :-) Fortunately, once (and if) it's defined (what ever it turns out to be) Python examples can be used to explain Python. ;-) Each case label expression would be evaluated when the switch block is executed, ie... in order it appears in the program, but the code for each case would be skipped until a (do choice in choices) line. Each switch case block would not fall through but return to the next line after the 'do' line by default. The whole thing could be put in a separate function or method if it's desired to get the single function call form you suggested along with a separate name space. def switcher(choice): switcher roo: 1: a = 42 42: a = 1 else: raise ValueError do choice in switcher: return a switcher(1) - 42 switcher(42) - 1 switcher(100) - raises exception I'm still unclear on when you propose the case expressions to be evaluated. Each time the switch statement is encountered? That would be the most natural given the rest of your explanation. But then a switch inside a function that references globally defined constants would be re-evalulated each time the function is called; much of the discussion here is focused on trying to reduce the number of times the switch cases are evaluated to once per program invocation or once per function *definition*. Each time the 'switch' statement is encountered for the above. Allowing static to be used with this form could be an option to force function define time evaluations of cases. ONE = 1 FOURTYTWO = 42 def switcher(choice): static switcher roo: # evaluate cases at function def time. ONE: a = 42 FOURTYTWO: a = 1 else: raise ValueError do choice in switcher: return a switcher(1) - 42 switcher(42) - 1 switcher(100) - raises exception That would give you both call time and def time evaluations for cases with clear behavior for both (I think). Also since the switch has a name, it might be possible to examine their values with dir(switch_suite_name). That might help in debugging and explaining the behavior in different situations. The 'case' keyword could be left off in this form because the switch body is always a suite. I find it more readable without it in the case of simple literals or named values, and more readable with it for more complex expressions. I don't think I can clarify this further without getting over my head. I probably am a bit already. I'm in the know enough to get myself in trouble (at times) group. ;-) Ron ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Ron Adam wrote: In this instance the switch would be redefined 10 times. The ending switch would be: switch S: case 10: print 42 Silly mistake correction... :) switch S: case 9: print 42 ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Guido van Rossum wrote: On 6/27/06, Ron Adam [EMAIL PROTECTED] wrote: So modeling the switch after dictionary dispatching more directly where the switch is explicitly defined first and then used later might be good both because it offers reuse in the current scope and it can easily be used in code that currently uses dict style dispatching. switch name: 1: ... TWO: ... 'a', 'b', 'c': ... in range(5,10): ... else: ... for choice in data: do choice in name:# best calling form I can think of. It looks like your proposal is to change switch into a command that defines a function of one parameter. Instead of the do expression in switch call you could just call the switch -- no new syntax needed. Your example above would be for choice in data: name(choice) # 'name' is the switch's name This parallels some of my thinking -- that we ought to somehow make the dict-building aspect of the switch statement explicit (which is better than implicit, as we all have been taught.) My version of this is to add to Python the notion of a simple old-fashioned subroutine - that is, a function with no arguments and no additional scope, which can be referred to by name. For example: def MyFunc( x ): sub case_1: ... sub case_2: ... sub case_3: ... # A direct call to the subroutine: do case_1 # An indirect call y = case_2 do y # A dispatch through a dict d = dict( a=case_1, b=case_2, c_case_3 ) do d[ 'a' ] The 'sub' keyword defines a subroutine. A subroutine is simply a block of bytecode with a return op at the end. When a subroutine is invoked, control passes to the indented code within the 'sub' clause, and continues to the end of the block - there is no 'fall through' to the next block. When the subroutine is complete, a return instruction is exected, and control transfers back to the original location. Because subroutines do not define a new scope, they can freely modify the variables of the scope in which they are defined, just like the code in an 'if' or 'else' block. One ambiguity here is what happens if you attempt to call a subroutine from outside of the code block in which it is defined. The easiest solution is to declare that this is an error - in other words, if the current execution scope is different than the scope in which the subroutine is defined, an exception is thrown. A second possibility is to store a reference to the defining scope as part of the subroutine definition. So when you take a reference to 'case_1', you are actually referring to a closure of the enclosing scope and the subroutine address. This approach has a number of advantages that I can see: -- Completely eliminates the problems of when to freeze the dict, because the dict is 'frozen' explicitly (or not at all, if desired.) -- Completely eliminates the question of whether to support ranges in the switch cases. The programmer is free to invent whatever type of dispatch mechanism they wish. For example, instead of using a dict, they could use an array of subroutines, or a spanning tree / BSP tree to represent contiguous ranges of options. -- Allows for development of dispatch methods beyond the switch model - for example, the dictionary could be computed, transformed and manipulated by user code before used for dispatch. -- Allows for experimentation with other flow of control forms. The primary disadvantage of this form is that the case values and the associated code blocks are no longer co-located, which reduces some of the expressive power of the switch. Note that if you don't want to define a new keyword, an alternate syntax would be 'def name:' with no argument braces, indicating that this is not a function but a procedure. -- Talin ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
On Mon, 26 Jun 2006, Guido van Rossum wrote: I've written a new PEP, summarizing (my reaction to) the recent discussion on adding a switch statement. While I have my preferences, I'm trying to do various alternatives justice in the descriptions. Thanks for writing this up! The section that most draws my attention is Semantics, and i guess it isn't a surprise to either of us that you had the most to say from the perspective you currently support (School II). :) Let me suggest a couple of points to add: - School I sees trouble in the approach of pre-freezing a dispatch dictionary because it places a new and unusual burden on the programmer to understand exactly what kinds of case values are allowed to be frozen and when the case values will be frozen. - In the School II paragraph you say Worse, the hash function might have a bug or a side effect; if we generate code that believes the hash, a buggy hash might generate an incorrect match -- but that is primarily a criticism of the School II approach, not of the School I approach as you have framed it. It's School II that mandates that the hash be the truth. (It looks to me like what you're actually criticizing here is based on some assumptions about how you think School I might be implemented, and having taken School I a number of steps down that (unexplained) road you then see problems with it.) Also, why is the discussion of School II mostly an argument against School I? What about describing the advantages of each school? -- ?!ng ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
On 6/26/06, Ka-Ping Yee [EMAIL PROTECTED] wrote: On Mon, 26 Jun 2006, Guido van Rossum wrote: I've written a new PEP, summarizing (my reaction to) the recent discussion on adding a switch statement. While I have my preferences, I'm trying to do various alternatives justice in the descriptions. Thanks for writing this up! The section that most draws my attention is Semantics, and i guess it isn't a surprise to either of us that you had the most to say from the perspective you currently support (School II). :) Let me suggest a couple of points to add: - School I sees trouble in the approach of pre-freezing a dispatch dictionary because it places a new and unusual burden on the programmer to understand exactly what kinds of case values are allowed to be frozen and when the case values will be frozen. Can you please edit the PEP yourself to add this? That will be most efficient. - In the School II paragraph you say Worse, the hash function might have a bug or a side effect; if we generate code that believes the hash, a buggy hash might generate an incorrect match -- but that is primarily a criticism of the School II approach, not of the School I approach as you have framed it. It's School II that mandates that the hash be the truth. You seem to misunderstand what I'm saying or proposing here; admittedly I think I left something out. With school I, if you want to optimize using a hash table (as in PEP 275 Solution 1) you have to catch and discard exceptions in hash(), and a bug in hash() can still lead this optimization astray: if A == B but hash(A) != hash(B), switch A: // case B: ... // else: ... may falsely take the else branch, thereby causing a hard-to-debug difference between optimized and unoptimized code. With school II, exceptions in hash() aren't caught or discarded; a bug in hash() leads to the same behavior as optimized school I, but the bug is not dependent on the optimization level. (It looks to me like what you're actually criticizing here is based on some assumptions about how you think School I might be implemented, and having taken School I a number of steps down that (unexplained) road you then see problems with it.) Right. School I appears just as keen as school II to use hashing to optimize things, but isn't prepared to pay the price in semantics; but I believe the optimizations are impossible to behave completely identically to the unoptimized code (not even counting side effects in hash() or __eq__()) so I believe the position that the optimized version is equivalent to the unoptimized official semantics according to school I is untenable. Also, why is the discussion of School II mostly an argument against School I? What about describing the advantages of each school? School II has the advantage of not incurring the problems I see with school I, in particular catching and discarding exceptions in hash() and differences between optimized and unoptimized code. -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
On Mon, 26 Jun 2006, Guido van Rossum wrote: Can you please edit the PEP yourself to add this? That will be most efficient. I've done so, and tried to clarify the next line to match (see below). With school I, if you want to optimize using a hash table (as in PEP 275 Solution 1) you have to catch and discard exceptions in hash(), and a bug in hash() can still lead this optimization astray Right. As written, the problem a buggy hash might generate an incorrect match is not specific to School I; it's a problem with any approach that is implemented by a hash lookup. School II is necessarily implemented this way; School I might or might not be. So i think the part that says: the hash function might have a bug or a side effect; if we generate code that believes the hash, a buggy hash might generate an incorrect match doesn't belong there, and i'd like your consent to remove it. On the other hand, this criticism: if we generate code that catches errors in the hash to fall back on an if/elif chain, we might hide genuine bugs is indeed specific to School I + hashing. Right. School I appears just as keen as school II to use hashing to optimize things, but isn't prepared to pay the price in semantics; Ok. Then there's an inconsistency with the definition of School I: School I wants to define the switch statement in term of an equivalent if/elif chain To clear this up, i've edited the first line of the School II paragraph, which previously said: School II sees nothing but trouble in that approach It seems clear that by that approach you meant trying to achieve if/elif semantics while using hash optimization rather than the more general definition of School I that was given. I believe there are a few voices here (and i count myself among them) that consider the semantics more important than the speed and are in School I but aren't treating hash optimization as the quintessence of 'switch', and we shouldn't leave them out. -- ?!ng ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
On 6/26/06, Ka-Ping Yee [EMAIL PROTECTED] wrote: On Mon, 26 Jun 2006, Guido van Rossum wrote: Can you please edit the PEP yourself to add this? That will be most efficient. I've done so, and tried to clarify the next line to match (see below). With school I, if you want to optimize using a hash table (as in PEP 275 Solution 1) you have to catch and discard exceptions in hash(), and a bug in hash() can still lead this optimization astray Right. As written, the problem a buggy hash might generate an incorrect match is not specific to School I; it's a problem with any approach that is implemented by a hash lookup. School II is necessarily implemented this way; School I might or might not be. So i think the part that says: the hash function might have a bug or a side effect; if we generate code that believes the hash, a buggy hash might generate an incorrect match doesn't belong there, and i'd like your consent to remove it. I'd rather keep it, but clarify that school II considers the outcome of the hash() the official semantics, while school I + dict-based optimization would create optimized code that doesn't match the language specs. My point being that not following your own specs is a much more severe sin than trusting a buggy hash(). If hash() is buggy, school II's official spec means that the bug affects the outcome, and that's that; but because school I specifies the semantics as based on an if/elif chain, using a buggy hash() means not following the spec. If we choose school I, a user may assume that a buggy hash() doesn't affect the outcome because it's defined in terms of == tests only. On the other hand, this criticism: if we generate code that catches errors in the hash to fall back on an if/elif chain, we might hide genuine bugs is indeed specific to School I + hashing. Right. Right. School I appears just as keen as school II to use hashing to optimize things, but isn't prepared to pay the price in semantics; Ok. Then there's an inconsistency with the definition of School I: School I wants to define the switch statement in term of an equivalent if/elif chain To clear this up, i've edited the first line of the School II paragraph, which previously said: School II sees nothing but trouble in that approach It seems clear that by that approach you meant trying to achieve if/elif semantics while using hash optimization rather than the more general definition of School I that was given. Right. Thanks for the clarification; indeed, the only problem I have with the clean school I approach (no hash-based optimization) is that there's no optimization, and we end up once more having to tweak the ordering of the cases based on our expectation of their frequency (which may not match reality). Most school I proponents (perhaps you're the only exception) have claimed that optimization is desirable, but added that it would be easy to add hash-based optimization. IMO it's not so easy in the light of various failure modes of hash(). (A possible solution would be to only use hashing if the expression's type is one of a small set of trusted builtins, and not a subclass; we can trust int.__hash__, str.__hash__ and a few others.) I believe there are a few voices here (and i count myself among them) that consider the semantics more important than the speed and are in School I but aren't treating hash optimization as the quintessence of 'switch', and we shouldn't leave them out. This is an important distinction; thanks for pointing it out. Perhaps we can introduce school Ia and Ib, where Ia is clean but unoptimized and Ib is if/elif with hash-based optimization desirable when possible. Another distinction between the two schools is that school Ib will have a hard time optimizing switches based on named constants. I don't believe that the callback if variable affecting expression value is ever assigned to approach will work. School II is definitely more pragmatist; I really don't see much wrong with defining that it works a certain way, which is not exactly what you would expect but has the same effect in *most* common cases, and then explaining odd behavior in various corner cases out of that way, as long as I don't care about those corner cases. This is pretty much how I defend the semantics of default parameter values -- it doesn't matter that they are computed at function definition time instead of call time as long as you only use immutable constants (which could be named constants as long as they're immutable), which is the only use case I care about. There are many other examples of odd Python semantics that favor a relatively simple implementation and glorify that implementation as the official semantics. I find this much preferable over weasel words a la traditional language standards which give optimizers a lot of leeway at the cost of difference in behavior between different compilers or optimization
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
On Mon, 26 Jun 2006, Guido van Rossum wrote: Most school I proponents (perhaps you're the only exception) have claimed that optimization is desirable, but added that it would be easy to add hash-based optimization. IMO it's not so easy in the light of various failure modes of hash(). (A possible solution would be to only use hashing if the expression's type is one of a small set of trusted builtins, and not a subclass; we can trust int.__hash__, str.__hash__ and a few others.) That's a good idea! At first glance, it seems like that could lead to a plausible compromise. -- ?!ng ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
Guido van Rossum wrote: Most school I proponents (perhaps you're the only exception) have claimed that optimization is desirable, but added that it would be easy to add hash-based optimization. IMO it's not so easy in the light of various failure modes of hash(). (A possible solution would be to only use hashing if the expression's type is one of a small set of trusted builtins, and not a subclass; we can trust int.__hash__, str.__hash__ and a few others.) that's the obvious approach for the optimize-under-the-hood school -- only optimize if you can do that cleanly (to enable optimization, all case values must be either literals or statics, have the same type, and belong to a set of trusted types). this gives a speedup in all main use cases, and clear semantics in all cases. another approach would be to treat switch/case and switch/case-static as slightly different constructs; if all cases are static, put the case values in a dictionary, and do the lookup as usual (propagating any errors). /F ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] PEP 3103: A Switch/Case Statement
On 6/26/06, Fredrik Lundh [EMAIL PROTECTED] wrote: Guido van Rossum wrote: Most school I proponents (perhaps you're the only exception) have claimed that optimization is desirable, but added that it would be easy to add hash-based optimization. IMO it's not so easy in the light of various failure modes of hash(). (A possible solution would be to only use hashing if the expression's type is one of a small set of trusted builtins, and not a subclass; we can trust int.__hash__, str.__hash__ and a few others.) that's the obvious approach for the optimize-under-the-hood school -- only optimize if you can do that cleanly (to enable optimization, all case values must be either literals or statics, have the same type, and belong to a set of trusted types). this gives a speedup in all main use cases, and clear semantics in all cases. another approach would be to treat switch/case and switch/case-static as slightly different constructs; if all cases are static, put the case values in a dictionary, and do the lookup as usual (propagating any errors). I think we need a PEP for const/static/only/cached/precomputed or whatever people like to call it. Once we have (say) static, I think making the case expressions static by default would still cover all useful cases, and would allow us to diagnose duplicate cases reliably (which the if/elif chain semantics don't allow IIUC). -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com