Yet Another Switch-Case Syntax Proposal
Hello everybody. I firstly proposed this syntax in the python-list. As I already wrote there, I read PEP 3103 and I'm not completely satisfied by any of the proposed solutions. My first idea was a little different, this is my final proposal after a short but good brainstorm: switch_stmt ::= "switch" switch_expr "case" case_expr ":" suite ("case" | "elcase" case_expr ":" suite)* ["else" ":" suite] switch_expr ::= expression case_expr ::= expression_list - if case_expr is a tuple, the case suite will be executed if switch_expr is a member of the tuple - if case_expr is not a tuple, the case suite will be executed if switch_expr == case_expr - if a case_expr is checked, any subsequent elcase statements are skipped, and the next case statement is performed, of there's one. This is completely identical to if - elif. Example: briefing_days = ("Tue", "Thu") normal_days = ("Mon", "Wed", "Fri") switch day case normal_days + briefing_days: go_to_work = True day_type = "weekday" case normal_days: lunch_time = datetime.time(12) meeting_time = datetime.time(14) elcase briefing_days: lunch_time = datetime.time(11, 30) meeting_time = datetime.time(12, 30) else: go_to_work = False day_type = "festive" lunch_time = None meeting_time =None A simpler example: switch tarot case 0: card = "Fool" elcase 1: card = "Alan Moore" elcase 2: card = "High Priestess" <etc....> Some remarks: 1. switch is on the same line of the first case. This is because alternatives in PEP 3103 seems unpythonic to me 2. I decided to not use already existing keywords like "if" or "in", since this will be misleading 3. I preferred case / elcase instead of using break or continue keyword because: a. break / continue can confuse, since they are used in loop statements, and this is not a loop statement, as Ethan Furman pointed out in the other mailing list b. break / continue is useful only if it can be used not only as final command, so you can skip the rest of the current case suite. If it must be the final command, it's the same of case - elcase. IMHO there's no much real need to allow this c. case - elcase syntax is much more readable and less confusing of a break / continue d. case - elcase syntax is identical to if - elif, so people will be more familiar with it 4. notice that you can put a "case" statement after an "elcase" one. For example: switch cpu case "A6-6400K", "A8-6600K" clock = 3.9 elcase "A10-6790K", "A10-6800B" clock = 4.1 case "A6-6400K" tdp = 65 elcase "A8-6600K", "A10-6790K", "A10-6800B" tdp = 100 is equivalent to if cpu in ("A6-6400K", "A8-6600K") clock = 3.9 elif cpu in ("A10-6790K", "A10-6800B") clock = 4.1 if cpu == "A6-6400K" tdp = 65 elif cpu in ("A8-6600K", "A10-6790K", "A10-6800B") tdp = 100
I don't want to discourage you too much, but I think that adding a switch statement comes *very* low on the list of improvements we would like to make in Python 3.5. We should probably focus on speed (or aspects of it, like startup), language features that help porting Python 2 code to it (e.g. bytes formatting), and things that improve the user experience of getting started with Python on a new machine (e.g. pip/venv). Or perhaps stdlib issues like an asyncio-infused variation of WSGI. I've probably missed a few focus areas, but I still very much doubt we'll be adding a switch statement -- it's a "sexy" language design issue (like anonymous functions) but that's not what will help Python compete. On Thu, Apr 17, 2014 at 12:14 PM, Lucas Malor <7vsfeu4pxg@snkmail.com>wrote:
Hello everybody. I firstly proposed this syntax in the python-list. As I already wrote there, I read PEP 3103 and I'm not completely satisfied by any of the proposed solutions.
My first idea was a little different, this is my final proposal after a short but good brainstorm:
switch_stmt ::= "switch" switch_expr "case" case_expr ":" suite ("case" | "elcase" case_expr ":" suite)* ["else" ":" suite] switch_expr ::= expression case_expr ::= expression_list
- if case_expr is a tuple, the case suite will be executed if switch_expr is a member of the tuple - if case_expr is not a tuple, the case suite will be executed if switch_expr == case_expr - if a case_expr is checked, any subsequent elcase statements are skipped, and the next case statement is performed, of there's one. This is completely identical to if - elif.
Example:
briefing_days = ("Tue", "Thu") normal_days = ("Mon", "Wed", "Fri")
switch day case normal_days + briefing_days: go_to_work = True day_type = "weekday" case normal_days: lunch_time = datetime.time(12) meeting_time = datetime.time(14) elcase briefing_days: lunch_time = datetime.time(11, 30) meeting_time = datetime.time(12, 30) else: go_to_work = False day_type = "festive" lunch_time = None meeting_time =None
A simpler example:
switch tarot case 0: card = "Fool" elcase 1: card = "Alan Moore" elcase 2: card = "High Priestess" <etc....>
Some remarks:
1. switch is on the same line of the first case. This is because alternatives in PEP 3103 seems unpythonic to me 2. I decided to not use already existing keywords like "if" or "in", since this will be misleading 3. I preferred case / elcase instead of using break or continue keyword because: a. break / continue can confuse, since they are used in loop statements, and this is not a loop statement, as Ethan Furman pointed out in the other mailing list b. break / continue is useful only if it can be used not only as final command, so you can skip the rest of the current case suite. If it must be the final command, it's the same of case - elcase. IMHO there's no much real need to allow this c. case - elcase syntax is much more readable and less confusing of a break / continue d. case - elcase syntax is identical to if - elif, so people will be more familiar with it 4. notice that you can put a "case" statement after an "elcase" one. For example:
switch cpu case "A6-6400K", "A8-6600K" clock = 3.9 elcase "A10-6790K", "A10-6800B" clock = 4.1 case "A6-6400K" tdp = 65 elcase "A8-6600K", "A10-6790K", "A10-6800B" tdp = 100
is equivalent to
if cpu in ("A6-6400K", "A8-6600K") clock = 3.9 elif cpu in ("A10-6790K", "A10-6800B") clock = 4.1
if cpu == "A6-6400K" tdp = 65 elif cpu in ("A8-6600K", "A10-6790K", "A10-6800B") tdp = 100 _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- --Guido van Rossum (python.org/~guido)
On 17 April 2014 21:31, Guido van Rossum guido-at-python.org | python-ideas-at-python.org| <gpb6b76ift@sneakemail.com> wrote:
I don't want to discourage you too much, but I think that adding a switch statement comes *very* low on the list of improvements we would like to make in Python 3.5.
We should probably focus on speed (or aspects of it, like startup), language features that help porting Python 2 code to it (e.g. bytes formatting), and things that improve the user experience of getting started with Python on a new machine (e.g. pip/venv).. Or perhaps stdlib issues like an asyncio-infused variation of WSGI.
I completely agree.
I've probably missed a few focus areas, but I still very much doubt we'll be adding a switch statement -- it's a "sexy" language design issue (like anonymous functions) but that's not what will help Python compete.
Well, no, it will not be a "killer feature". But if you intend it's "sexy" because it's a syntax sugar, even if I'm not much experienced, I respectfully disagree. A switch-case statement is more DRY than an if-elif chain, in its use environment. Deltas are only quicker to code and potentially less DRY. On 17 April 2014 23:41, Andrew Barnert abarnert-at-yahoo.com |
python-ideas-at-python.org| <3en9kh2cbt@sneakemail.com> wrote: Any reason to use the keywords that only exist in C-family languages when the functionality isn't actually like C?
Well, it's the same for the "for" statement.
- if case_expr is not a tuple, the case suite will be executed if switch_expr == case_expr
- if a case_expr is checked, any subsequent elcase statements are skipped, and the next case statement is performed, of there's one. This is completely identical to if - elif.
While you can figure out what this means with a bit of work, it seems to add a lot of conceptual complexity, and I can't think of any other language that does this.
Bash case statement is similar. IMHO this syntax is very simple, simpler than C-like switch statements. Why do you think it is complex? why not consider features that other languages do have, like pattern
matching cases?
I do not know much about pattern matching. Using my little knowledge, Python have recursive functions and regular expressions. You could also use generator expressions with my switch statement proposal. Symbolic pattern is powerful, but it's a bit too much for this little proposal. if you want to match (1, 'b') you'd have to write case ((1, 'b'),). Which
is bad for static cases, and even worse for dynamic ones.
Yes, it's not so elegant, but I do not think that it will really matters in real world usage. What do you mean exactly with "dynamic cases"?
On Apr 18, 2014, at 7:42, "Lucas Malor" <7vsfeu4pxg@snkmail.com> wrote:
On 17 April 2014 23:41, Andrew Barnert abarnert-at-yahoo.com |python-ideas-at-python.org| <3en9kh2cbt@sneakemail.com> wrote: Any reason to use the keywords that only exist in C-family languages when the functionality isn't actually like C?
Well, it's the same for the "for" statement.
No it isn't. First, the "for ... in" keywords are not the same as just "for" and a bunch of parens and semicolons. If you just mean the word "for" itself, the exact same word is used in a wide variety of languages that have nothing to do with C, and some of them use it with the same meaning as Python. By contrast, most languages that are not related to C don't spell this statement "switch ... case ..."; the most common is probably "case ... of ..." or "case ... in ...". Also, notice that if you try to read the switch statement, or your Python version, as English, it's nonsense. Compare that to for, while, if, try, etc. While there are some oddities (like condensing "else" and "if" into "elif"), as a general rule they make sense. That's not even remotely true with a C for statement.
- if case_expr is not a tuple, the case suite will be executed if switch_expr == case_expr - if a case_expr is checked, any subsequent elcase statements are skipped, and the next case statement is performed, of there's one. This is completely identical to if - elif.
While you can figure out what this means with a bit of work, it seems to add a lot of conceptual complexity, and I can't think of any other language that does this.
Bash case statement is similar.
Bash doesn't have separate "case" and "elcase" cases. After one case is done, the rest are skipped, just as in most other languages.
IMHO this syntax is very simple, simpler than C-like switch statements. Why do you think it is complex?
C cases always fall through to the next case. Almost every other language only executes a single case. I don't see how skipping over any elcase but falling through to the next case is in any way simpler than C.
why not consider features that other languages do have, like pattern matching cases?
I do not know much about pattern matching.
Well, then at least look at the limited form of pattern matching Python has in the for and assignment statements and parameter matching, and maybe look at how pattern matching is used with case statements in other languages; don't try to suggest language designs based on guesses.
Using my little knowledge, Python have recursive functions and regular expressions.
... and? Are you suggesting that if the switch expression is a string and the case expression a compiled regex you could automatically call match instead of testing for equality? If not, how is having regexp even relevant here? And how are recursive functions relevant?
You could also use generator expressions with my switch statement proposal.
How? Where? A generator expression is equal to anything except itself, and doesn't contain anything.
Symbolic pattern is powerful, but it's a bit too much for this little proposal.
I don't know what you mean by "symbolic pattern" here.
if you want to match (1, 'b') you'd have to write case ((1, 'b'),). Which is bad for static cases, and even worse for dynamic ones.
Yes, it's not so elegant, but I do not think that it will really matters in real world usage.
Really? Have you never wanted to switch on the results of a function that returns two values?
What do you mean exactly with "dynamic cases"?
Exactly what it sounds like: when the case value is a variable set somewhere else in the program, possibly by some program calling my library or even selected by user input or config, so at the point of the switch statement you (meaning a reader of the code) have no idea whether the value is a tuple; it's just some variable or lookup expression or the like.
On Apr 17, 2014, at 12:14, "Lucas Malor" <7vsfeu4pxg@snkmail.com> wrote:
switch_stmt ::= "switch" switch_expr "case" case_expr ":" suite ("case" | "elcase" case_expr ":" suite)* ["else" ":" suite] switch_expr ::= expression case_expr ::= expression_list
Any reason to use the keywords that only exist in C-family languages when the functionality isn't actually like C?
- if case_expr is a tuple, the case suite will be executed if switch_expr is a member of the tuple
So there's no way to switch on a tuple?
- if case_expr is not a tuple, the case suite will be executed if switch_expr == case_expr - if a case_expr is checked, any subsequent elcase statements are skipped, and the next case statement is performed, of there's one. This is completely identical to if - elif.
While you can figure out what this means with a bit of work, it seems to add a lot of conceptual complexity, and I can't think of any other language that does this. Meanwhile, if you're trying to design a better and more powerful switch statement than other languages have, why not consider features that other languages do have, like pattern matching cases? Maybe it would be too hard to implement, or would conflict with another part of the feature, or just wouldn't fit in with Python, but if so, a switch proposal should try to explain that.
While I don't think it's a good addition for Python... On Thu, Apr 17, 2014 at 02:41:56PM -0700, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote:
On Apr 17, 2014, at 12:14, "Lucas Malor" <7vsfeu4pxg@snkmail.com> wrote:
switch_stmt ::= "switch" switch_expr "case" case_expr ":" suite ("case" | "elcase" case_expr ":" suite)* ["else" ":" suite] switch_expr ::= expression case_expr ::= expression_list
- if case_expr is a tuple, the case suite will be executed if switch_expr is a member of the tuple
So there's no way to switch on a tuple?
A tuple can be a member of a bigger tuple:
(1, 'b') in ((1, 'a'), (1, 'b'), (1, 'a', 'b')) True
Oleg. -- Oleg Broytman http://phdru.name/ phd@phdru.name Programmers don't die, they just GOSUB without RETURN.
On Apr 17, 2014, at 14:53, Oleg Broytman <phd@phdru.name> wrote:
While I don't think it's a good addition for Python...
On Thu, Apr 17, 2014 at 02:41:56PM -0700, Andrew Barnert <abarnert@yahoo.com.dmarc.invalid> wrote:
On Apr 17, 2014, at 12:14, "Lucas Malor" <7vsfeu4pxg@snkmail.com> wrote:
switch_stmt ::= "switch" switch_expr "case" case_expr ":" suite ("case" | "elcase" case_expr ":" suite)* ["else" ":" suite] switch_expr ::= expression case_expr ::= expression_list
- if case_expr is a tuple, the case suite will be executed if switch_expr is a member of the tuple
So there's no way to switch on a tuple?
A tuple can be a member of a bigger tuple:
(1, 'b') in ((1, 'a'), (1, 'b'), (1, 'a', 'b')) True
Well, yeah, but if you want to match (1, 'b') you'd have to write case ((1, 'b'),). Which is bad for static cases, and even worse for dynamic ones.
On 17 April 2014 16:14, Lucas Malor <7vsfeu4pxg@snkmail.com> wrote:
A simpler example:
switch tarot case 0: card = "Fool" elcase 1: card = "Alan Moore" elcase 2: card = "High Priestess" <etc....>
It may be just me, but I fail - in a complete manner - to see how this syntax can offer any improvement on current if/elif chains. It seems to differ from that only by reducing the explicitness, and the flexibility of the test condition that can be used in any of the subconditions, No, sorry, unlike others have mentioned, I don't find this particularly "sexy". "Clumky" is more like it: if tarot ==0: card = "Fool" elif tarot == 1: card = "Alan Moore" elif 2 < tarot < 22: # expressiveness card = "Surprise" else: raise ValueError The implicit "in" operation case is even more weird. js -><-
On Fri, Apr 18, 2014 at 10:03 AM, Joao S. O. Bueno <jsbueno@python.org.br> wrote:
It may be just me, but I fail - in a complete manner - to see how this syntax can offer any improvement on current if/elif chains.
I haven't been following this thread closely, so I don't know if Lucas has mentioned more than syntax, however... In other languages, the semantics of the switch statement allow the compiler to generate more efficient code. Instead of testing each branch of the if statement in succession looking for a match, you evaluate the switch expression once, then use it as an index into a jump table of some sort which points directly to the matching case label. If nothing matches, you jump to the default branch (if one was defined) or just jump over the entire switch statement (if not). Skip
I should also mention that the use of a jump table depends on the use of case labels which can be evaluated at compile time. This might make it challenging (or impossible) for this scheme to be implemented in Python. Skip
Skip Montanaro writes:
In other languages, the semantics of the switch statement allow the compiler to generate more efficient code. Instead of testing each branch of the if statement in succession looking for a match, you evaluate the switch expression once, then use it as an index into a jump table of some sort which points directly to the matching case label.
Sure, but Python already has such a jump table: a function-valued hash. Don't-even-think-of-mentioning-Ruby-blocks-now-ly y'rs,
On 18 April 2014 16:22, Skip Montanaro <skip@pobox.com> wrote:
On Fri, Apr 18, 2014 at 10:03 AM, Joao S. O. Bueno <jsbueno@python.org.br> wrote:
It may be just me, but I fail - in a complete manner - to see how this syntax can offer any improvement on current if/elif chains.
I haven't been following this thread closely, so I don't know if Lucas has mentioned more than syntax, however...
In other languages, the semantics of the switch statement allow the compiler to generate more efficient code. Instead of testing each branch of the if statement in succession looking for a match, you evaluate the switch expression once, then use it as an index into a jump table of some sort which points directly to the matching case label. If nothing matches, you jump to the default branch (if one was defined) or just jump over the entire switch statement (if not).
In which case you compile in the switch values at "compile time", so the semantics are "surprising" (if you switch on a name instead of a fixed value then changing the object the name is bound to doesn't change the behaviour of the switch). So you either get fast but surprising (unpythonic) or dynamic and no different to an if/elif chain. If you restrict switch to hashable literals then you can make it fast, and nicer syntactically than the standard pattern - building a dictionary of functions. But that's quite a restriction. (You basically note this in your follow up email - but this has been the problem with previous discussions of switch/case for Python.) Michael
Skip _______________________________________________ Python-ideas mailing list Python-ideas@python.org https://mail.python.org/mailman/listinfo/python-ideas Code of Conduct: http://python.org/psf/codeofconduct/
-- http://www.voidspace.org.uk/ May you do good and not evil May you find forgiveness for yourself and forgive others May you share freely, never taking more than you give. -- the sqlite blessing http://www.sqlite.org/different.html
Here's a simple class that provides case like syntax. How would modifying the language be better than this? This code makes it clear that all the checks are intended to be against the same value. It can easily be extended to have structural matching or regex matching. I think case/elcase would be confusing as it would be unique to python and it's backwards -- the elcase keywords is used for the normal way that people use case statements while the case keyword implements the broken code that inexperienced C programmers frequently write. With this class, you use if/elif/else exactly the way you usually do, and notice that there is no ambiguity when checking multiple values. class Case(object): def __init__(self, value): self.value = value def __enter__(self): return self def __exit__(self, exc_type, exc_value, traceback): pass def __call__(self, *values): return self.value in values def __eq__(self, value): return self.value == value def __lt__(self, value): return self.value < value def __gt__(self, value): return self.value > value # etc. # can easily add structural matching or regex matching as well for i in range(5): print(i, end=' => ') with Case(i) as case: if case(1): print('one') elif case((2,3)): print('tuple(two, three)') elif case(2, 3): print('two or three') elif case > 3: print('more than three') else: print('unmatched') produces 0 => unmatched 1 => one 2 => two or three 3 => two or three 4 => more than three Is it worth using a context manager for this? Maybe not. But if not, I think it's even less worthwhile to modify the language to add a case statement. --- Bruce Learn how hackers think: http://j.mp/gruyere-security https://www.linkedin.com/in/bruceleban
Python already supports switch...case statements. with switch(foo) as case: with case(10): print('foo is 10') with case.in_(15, 16, 17): print('foo might be 15 or 16 or 17') with case.default: print('Whatever...') You just need to implement the switch and case things to match the code above... Also, an if..elif..else block will be simpler to use
On 17/04/2014 20:14, Lucas Malor wrote: [snipped] There are any one of five or six recipes on the web that are adequate for my needs, I've never seen a proposal that I really like and Python has survived for 23 years without one. Can we therefore shelve the switch-case debate until, say, Py5k? Yes, I meant 5 :) -- My fellow Pythonistas, ask not what our language can do for you, ask what you can do for our language. Mark Lawrence --- This email is free from viruses and malware because avast! Antivirus protection is active. http://www.avast.com
participants (11)
-
Andrew Barnert
-
Bruce Leban
-
Guido van Rossum
-
Joao S. O. Bueno
-
João Bernardo
-
Lucas Malor
-
Mark Lawrence
-
Michael Foord
-
Oleg Broytman
-
Skip Montanaro
-
Stephen J. Turnbull