<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Fri, May 20, 2016 at 5:48 AM, Franklin? Lee <span dir="ltr"><<a href="mailto:leewangzhong+python@gmail.com" target="_blank">leewangzhong+python@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">I think there should be different syntaxes for matching equality and<br>
binding patterns, and definitely different syntax for singular and<br>
plural cases.<br>
<br>
Let's try this:<br>
- Equality:<br>
`case 5:`<br>
- Conditional:<br>
`case if predicate(obj):`<br>
- Pattern-matching:<br>
`case as [a, b, *_]:`<br>
`case as Point(x, y):`<br>
<br>
Slightly-more-explicit checks, instead of simply `case 5:`.<br>
- `case == 5:`<br>
- `case is _sentinel:`<br>
- `case is None:`<br>
- `case < 6:`<br>
- `case in (1,2,3):`<br>
- `case in range(2, 5):`<br>
- `case in int:`<br>
<br></blockquote><div><br></div><div>I personally really like the equality, conditional, and pattern-matching syntax you showed; it looks the most Pythonic IMO.</div><div><br></div><div>However, the more explicit checks are a little overkill and feel like too much special-casing. I don't know of any languages that go so far as to allow stuff like `case < 6`; usually, there would instead be some syntax to assign the initial object to an intermediate variable, that way you can just use it with an `if` predicate.</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">
The above uses an implicit object and is very different from how<br>
Python usually works. Though I think it's mentally consistent (as in,<br>
no contradictions), it's not an extrapolation of current syntax, and<br>
might go against "explicit self". It isn't necessary, though: require<br>
`case 5` and `case if obj == 5:`. I prefer `case == 5` and `case is<br>
5`, though, even if that's not how other languages do it.<br>
<br>
The last one treats a type as a collection of its instances, which I<br>
just like. (I also like `SubClass < BaseClass`.)<br>
<span class=""><br>
<br>
On Thu, May 19, 2016 at 4:20 AM, Greg Ewing <<a href="mailto:greg.ewing@canterbury.ac.nz">greg.ewing@canterbury.ac.nz</a>> wrote:<br>
> If we want this feature, it seems like we will need to<br>
> explicitly mark either names to be bound or expressions to<br>
> be evaluated and matched against.<br>
<br>
</span>Possible pre-fix markers:<br>
case as Point(zero, =y):<br>
case as Point(zero, !y):<br>
case as Point(zero, $y):<br>
case as Point(zero, &y):<br>
case as Point(zero, ^y):<br>
case as Point(zero, :y):<br>
<br>
Possible post-fix markers:<br>
case as Point(zero, y=):<br>
case as Point(zero, y!):<br>
case as Point(zero, y:):<br>
<br>
I think the colon won't be ambiguous in dict displays (it's only a<br>
marker if one side of the colon is missing), but it might complicate<br>
the parser. It'd be ambiguous in slicing, but I don't see<br>
slice-matching being a thing.<br>
<br>
(My favorite is probably `&y`, for a bad reason: it's very C-like.<br>
That'd confuse C programmers when `&` doesn't work anywhere else and<br>
`*` won't work at all.)<br>
<span class=""><br>
<br>
> If you wanted the second case to only match the tuple,<br>
> you could write<br>
><br>
> case tuple(a, b, *_):<br>
<br>
</span>Slightly naughty. The tuple constructor only takes one argument.<br>
<span class=""><br>
<br>
On Thu, May 19, 2016 at 12:15 AM, Guido van Rossum <<a href="mailto:guido@python.org">guido@python.org</a>> wrote:<br>
> A few things that might be interesting to explore:<br>
<br>
</span>I'll try out my syntax ideas on these.<br>
<span class=""><br>
> - match by value or set of values (like those PEPs)<br>
<br>
</span> `case == 1:`<br>
`case in (1, 2, 3):`<br>
<span class=""><br>
> - match by type (isinstance() checks)<br>
<br>
</span> `case in int:`<br>
`case if isinstance(obj, int):`<br>
`case if type(obj) == tuple:`<br>
<span class=""><br>
> - match on tuple structure, including nesting and * unpacking<br>
> (essentially, try a series of destructuring assignments until one<br>
> works)<br>
<br>
</span> `case in tuple as (first, *middle, last):`<br>
`case if isinstance(obj, tuple) as (first, *middle, last):`<br>
`case if type(obj) == tuple as (first, *middle, last):`<br>
<span class=""><br>
> - match on dict structure? (extension of destructuring to dicts)<br>
<br>
</span> I think it makes no sense to bind the keys to names, because<br>
they'd just be chosen arbitrarily (unless it's an OrderedDict), so<br>
let's say keys are evaluated and values are (by default) names to<br>
bind.<br>
<br>
Let `**_` mean "other items".<br>
<br>
`case as {'foo': x, 'bar': y, **_}:`<br>
`case as {key0: val0, key1: val1}: # Binds val0 and val1.`<br>
`case as {'foo': foo_val, var_holding_bar: bar_val, **_}:`<br>
^ Ew. I'd like names-to-bind to require special syntax.<br>
`case as dict(foo=foo_val, **{var_holding_bar: bar_val}, **_):`<br>
<br>
If we had an OrderedDict syntax, binding keys makes more sense.<br>
case as [k0: v0, **_, klast: vlast]:<br>
<br>
P.S.: If `**_` is allowed in matching, it should be allowed in<br>
unpacking assignment.<br>
{'foo': x, 'bar': y, **_} = d<br>
<span class=""><br>
<br>
> - match on instance variables or attributes by name?<br>
<br>
</span> One of the following:<br>
`case as object(foo=x, bar=y):`<br>
`case as Object(foo=x, bar=y):`<br>
`case as SpecialAttrMatchingThing(foo=x, bar=y):`<br>
<br>
`SpecialAttrMatchingThing` objects would be special in the match system.<br>
<span class=""><br>
> - match on generalized condition (predicate)?<br>
<br>
</span> `case <= 4:`<br>
`case if is_something(the_obj):`<br>
`case as Point(x, y) if x == y:`<br>
`case if len(the_obj) < 5 as [first, second, *_] if isinstance(first, int):`<br>
<br>
<br>
== Matching user classes ==<br>
<br>
What about using pickle dunders for this? In particular,<br>
`MyClass.__getnewargs__` and `MyClass.__getnewargs_ex__`. Pickling is<br>
kind of related to pattern matching. Note that the classes don't have<br>
to be immutable to be matchable.<br>
<br>
When matching `p` to `Point(x, y)`, the match system calls<br>
`Point.__getnewargs__(p)` (NOT `p.__getnewargs__`, thus allowing for<br>
some subclass matching). The result is matched against a two-tuple,<br>
and finally bound.<br>
<br>
# def match_constructor(obj, cls):<br>
if not isinstance(obj, cls):<br>
raise FailedMatch<br>
try:<br>
m = cls.__getnewargs_ex__<br>
except AttributeError:<br>
pass<br>
else:<br>
args, kwargs = m(obj) # Even for subclasses.<br>
return args, kwargs<br>
try:<br>
m = cls.__getnewargs__<br>
except AttributeError:<br>
raise FailedMatch<br>
args = m(obj)<br>
return args, {}<br>
<br>
# Back in the case:<br>
try:<br>
(x, y), [] = match_constructor(p, Point)<br>
except ValueError:<br>
raise FailedMatch<br>
run_block()<br>
<br>
<br>
The problem is that these functions return (args, kwargs), and<br>
optional args and pos_or_kw_params mess things up.<br>
<br>
Point(x, y)<br>
Point(x, y=y)<br>
# How can match system know that all of the following are<br>
valid patterns?<br>
Something(x)<br>
Something(x, y)<br>
Something(*args, **kwargs)<br>
<br>
Solution 1: Additional args to the pickle methods (or make a new<br>
method which could sometimes be used for pickling):<br>
- nargs: Number of args. An int, or a integer range (for `*args`).<br>
A `range` doesn't allow unbounded, so use `slice(min_nargs, None)`,<br>
`(min_nargs, ...)`, or `(min_nargs, None)`.<br>
- kws: Keywords. To represent **kwargs, either add another arg or<br>
pass `...` or `None` as a keyword.<br>
<br>
Solution 2: After receiving (args, kwargs) from the class, inspect the<br>
signature of the class constructor and just figure out how it works.<br>
<br>
<br>
== Additional Resources ==<br>
<br>
* MacroPy implements case classes, which are similar to what Haskell<br>
uses for pattern matching on constructors. I'm not sure that it lends<br>
insight to the `Point(x, y)` case, because MacroPy doesn't have<br>
pattern matching, but maybe someone else would.<br>
<a href="https://github.com/lihaoyi/macropy#case-classes" rel="noreferrer" target="_blank">https://github.com/lihaoyi/macropy#case-classes</a><br>
<br>
* "Pattern matching in Python" (2009)<br>
An attempt at making a matching thing.<br>
<a href="https://monkey.org/~marius/pattern-matching-in-python.html" rel="noreferrer" target="_blank">https://monkey.org/~marius/pattern-matching-in-python.html</a><br>
<br>
* PEP 275 -- Switching on Multiple Values<br>
<a href="https://www.python.org/dev/peps/pep-0275/" rel="noreferrer" target="_blank">https://www.python.org/dev/peps/pep-0275/</a><br>
<br>
* PEP 3103 -- A Switch/Case Statement<br>
<a href="https://www.python.org/dev/peps/pep-3103/" rel="noreferrer" target="_blank">https://www.python.org/dev/peps/pep-3103/</a><br>
<div class="HOEnZb"><div class="h5">_______________________________________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org">Python-ideas@python.org</a><br>
<a href="https://mail.python.org/mailman/listinfo/python-ideas" rel="noreferrer" target="_blank">https://mail.python.org/mailman/listinfo/python-ideas</a><br>
Code of Conduct: <a href="http://python.org/psf/codeofconduct/" rel="noreferrer" target="_blank">http://python.org/psf/codeofconduct/</a><br>
</div></div></blockquote></div><br><br clear="all"><div><br></div>-- <br><div class="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div dir="ltr"><div dir="ltr">Ryan</div><div dir="ltr">[ERROR]: Your autotools build scripts are 200 lines longer than your program. Something’s wrong.<br></div><div dir="ltr"><div><a href="http://kirbyfan64.github.io/" target="_blank">http://kirbyfan64.github.io/</a><div style="display:inline-block;width:16px;height:16px"> </div></div></div></div></div></div></div></div>
</div></div>