Over time, there have been various switch or match statement proposal; some that have gotten as far as PEPs:
2001 Nov - https://www.python.org/dev/peps/pep-0275/
2006 Jun - https://www.python.org/dev/peps/pep-3103/
2014 Apr - https://groups.google.com/d/msg/python-ideas/J5O562NKQMY/DrMHwncrmIIJ
2016 May - https://groups.google.com/d/msg/python-ideas/aninkpPpEAw/wCQ1IH5mAQAJ
However, I don't see that the conversation ever really resolved, so I'd like restart the conversation on some kind of pattern matching syntax in Python.
The main objections I've seen are in the following buckets:
I cannot handle all syntax objections ahead of time, but I can handle the "only way" objection. At high level, pattern matching provides similar syntactical sugar to list comprehensions. We could argue that they are unnecessary since we have for loops. But more importantly, pattern matching is powerful for what it restricts you to. More specifically:
I figured maybe a good way to continue the discussion is to offer a straw-man example syntax:
# Simple pattern matching x = 1 number = match x: 1 => "one" 2 => "two" 3 => "three" 10 => "ten" _ => "anything" print(number) # one # Final Pattern that matches anything x = 3 number = match x: 1 => "one" 2 => "two" _ => "anything" print(number) # anything # Pattern matching without any match returns None number = match x: 1 => "one" 2 => "two" print(number) # None # Pattern matching with guards x = 'three' number = match x: 1 => "one" y if y is str => f'The string is {y}' _ => "anything" print(number) # The string is three # Pattern matching with multiple values x = 1 number = match x: 1, 2, 3, 4 => "one to four" _ => "anything" print(number) # one to four # Pattern matching with types x = 1. number = match x: x:int => f'{x} is a int' x:float => f'{x} is a float' x:str => f'{x} is a string' print(number) # x is a float # Supports destructuring dicts x = {'foo': 1} number = match x: {'foo': 1} => "foo is 1" _ => "anything" print(number) # foo is 1 # Supports binding with destructuring dicts x = {'foo': 1, 'bar': 2} number = match x: {'foo': y} => f'got foo {y}' {'bar': z} => f'got bar {z}' {'foo': y, 'bar': z} => f'got foo {y} and bar {z}' _ => "anything" print(number) # got foo 1 and bar 2 # Supports destructuring other types too class Point(): def __init__(self, x, y): self.x = x self.y = y point = Point(1,2) number = match point: Point(x,y) => f'point has an x of {x} and y of {y}' _ => "anything" print(number) # point has an x of 1 and y of 2
As a continued defense for this specific syntax choixe, lets see how two other languages with this feature handle it. I'm going to try to offer as nearly as possible similar examples.
Scala https://docs.scala-lang.org/tour/pattern-matching.html
val x: Int = 1
def makeMatch(x: Any) = x match {
case 1 => "one"
case 2 => "two"
case _ => "anything"
}
val number = makeMatch(x)
Rust https://doc.rust-lang.org/1.5.0/book/match.html
let x = 1;
let number = match x {
1 => "one",
2 => "two",
_ => "anything",
}
And for the sake of completeness, here are other languages with similar syntax features and their associated documentation
F# https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/pattern-matching
Elixir https://elixir-lang.org/getting-started/case-cond-and-if.html
Clojure https://github.com/clojure/core.match/wiki/Basic-usage
JavaScript (ES2018?) https://github.com/tc39/proposal-pattern-matching
Haskell https://en.wikibooks.org/wiki/Haskell/Pattern_matching
And for the sake of completeness, here are other languages with similar syntax features and their associated documentation [...]
Stéfane> On Thu, May 3, 2018 at 2:41 PM, Robert Roskam <raider...@gmail.com>
Stéfane> wrote:
>>
>> And for the sake of completeness, here are other languages with similar
>> syntax features and their associated documentation [...]
>>
Stéfane> Still for the sake of completeness, and without any judgement from me at
Stéfane> this point, a couple more, which are more related to Python:
Stéfane> Coconut: http://coconut.readthedocs.io/en/master/DOCS.html#match
Stéfane> Mochi: https://github.com/i2y/mochi#pattern-matching
There's also macropy http://macropy3.readthedocs.io/en/latest/pattern.html
--
Alberto Berti - Information Technology Consultant
"gutta cavat lapidem"
I think you meant to use isinstance(y, str) ?
This looks like an incomplete ternary as well, missing the else
clause, so it wouldn't be a valid expression either way.
And a NameError. y is never defined anywhere. Since there's no name
bound to the variable being matched, would that mean a new keyword ?
Also, in the rest you seem to be testing "x == {matchidentifier}", but
here it suddenly looks like a boolean True would trigger a match ? And
if so, would other boolean truthy values also trigger one, making the
entire construct rather....limited ?
It looks like you're attempting to suggest at least 3 new language
syntax elements here. 4 if I count the type matching of "x:int", which
you could sell as type annotation, if those hadn't been explicitly
optional and ignored when actually binding the names.
And almost every other example can be solved with a dict and .get().
The remainder uses a dict as match, and still work on if/elif
perfectly fine.
Also, I'd say "but other people do it" isn't a valid reason for
implementation. There's plenty people doing stupid things, that
doesn't mean it's a good idea to do it to. If they idea can't stand on
it's own, it's not worth it.
From the syntax corner, it also doesn't really look like Python to me.
(my apologies if I sound a bit hostile. I've attempted 3 rewrites to
get that out. I only really tried to look at the syntax with what I
suppose is it's intended meaning here.)
# Existing Production Code
from datetime import timedelta, date
from django.utils import timezone
def convert_time_to_timedelta(unit:str, amount:int, now:date):
if unit in ['days', 'hours', 'weeks']:
return timedelta(**{unit: amount})
elif unit == 'months':
return timedelta(days=30 * amount)
elif unit == 'years':
return timedelta(days=365 * amount)
elif unit == 'cal_years':
return now - now.replace(year=now.year - amount)
# New Syntax for same problem
def convert_time_to_timedelta_with_match(unit:str, amount:int, now:date):
return match unit:
'days', 'hours', 'weeks' => timedelta(**{unit: amount})
'months' => timedelta(days=30 * amount)
'years' => timedelta(days=365 * amount)
'cal_years' => now - now.replace(year=now.year - amount)
That's buggy code in either certain. A month is not necessarily 30 days, and a year is not necessarily 365 days. An example without such painful bugs might be more compelling... It also likely makes the use case less obvious for the bug-free version.
On Thu, May 3, 2018, 2:37 PM Robert Roskam <raider...@gmail.com> wrote:
This was my initial response until I read the further examples that
cannot be done with a dict.
--
Terry Jan Reedy
1 Fore St, London, EC2Y 9DT
Machinalis Limited is a company registered in England and Wales. Registered number: 10574987.
I'll make a start, and you can correct me if I get any of it wrong.
(1) Pattern matching takes a value, and compares it to a series of
*patterns* until the first match, at which point it returns a specified
value, skipping the rest of the patterns.
(2) Patterns typically are single values, and the match is by equality,
although other kinds of patterns are available as well.
(3) Unlike a case/switch statement, there's no implication that the
compiler could optimise the order of look-ups; it is purely top to
bottom.
(4) Unlike if...elif, each branch is limited to a single expression, not
a block. That's a feature: a match expression takes an input, and
returns a value, and typically we don't have to worry about it having
side-effects.
So it is intentionally less general than a chain of if...elif blocks.
(5) We could think of it as somewhat analogous to a case/switch
statement, a dict lookup, or if...elif, only better.
(Why is it better?)
Here is a list of patterns I would hope to support, off the top of my
head:
* match by equality;
* match by arbitrary predicates such as "greater than X" or
"between X and Y";
* match by string prefix, suffix, or substring;
* match by type (isinstance).
I think that before we start talking about syntax, we need to know what
features we need syntax for.
There's probably more to it, because so far it doesn't look like
anything but a restricted switch statement. Over to someone else with a
better idea of why pattern matching has become ubiquitous in functional
programming.
I would like to see such examination for PEP 572. And for all other
syntax changing ideas.
I withdrew some my ideas and patches when my examinations showed that
the number of cases in the stdlib that will take a benefit from
rewriting using a new feature or from applying a compiler optimization
is not large enough.
Could you please give links to these results? It is hard to find
something in hundreds of messages.
match instr:
case Gate(name, params, qubits): result.append(Gate(name, params, [qubit_mapping[q] for q in qubits])
case Measurement(qubit, classical_reg): result.append(Measurement(qubit_mapping[qubit], classical_reg)
else: result.append(instr)
target = {'galaxy': {'system': {'planet': 'jupiter'}}}
# Risks getting a ValueError
my_planet = target[‘galaxy’][‘system’][‘planet’]
print(my_planet)
# Awkward to read
my_planet = target.get(‘galaxy’, {}).get(‘system’, {}).get(‘planet’)
if my_planet:
print(my_planet)
match target:
case {‘galaxy’: {‘system’: {‘planet’: my_planet}}}: print(my_planet)
--
---
You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/nqW2_-kKrNg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to python-ideas...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
_______________________________________________
Python-ideas mailing list
Python...@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/
--
---
You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/nqW2_-kKrNg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to python-ideas...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Hey Steven,I'm also at PyCon. Shall we take this off list and attempt to meet up and discuss?
--
---
You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/nqW2_-kKrNg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to python-ideas...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
_______________________________________________
Python-ideas mailing list
Python...@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/
Great! Also emailed you with logistics.
On Sat, May 12, 2018, 01:43 Jelle Zijlstra <jelle.z...@gmail.com> wrote:
--2018-05-11 22:01 GMT-04:00 Robert Roskam <raider...@gmail.com>:Hey Steven,I'm also at PyCon. Shall we take this off list and attempt to meet up and discuss?I'm also at PyCon and interested in meeting about this. I just wrote up a basic and incomplete implementation for pattern-matching yesterday between and after: talks: https://github.com/JelleZijlstra/cpython/blob/matchcase/Lib/test/test_matching.py. It's nowhere near complete, but an implementation like this can help inform what the syntax should look like.
---
You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/nqW2_-kKrNg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
_______________________________________________
Python-ideas mailing list
Python...@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/
--
---
You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/nqW2_-kKrNg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to python-ideas+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
To unsubscribe from this group and all its topics, send an email to python-ideas...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
_______________________________________________
Python-ideas mailing list
Python...@python.org
https://mail.python.org/mailman/listinfo/python-ideas
Code of Conduct: http://python.org/psf/codeofconduct/
--
---
You received this message because you are subscribed to a topic in the Google Groups "python-ideas" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/python-ideas/nqW2_-kKrNg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to python-ideas...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.