[Python-ideas] Match statement brainstorm
Franklin? Lee
leewangzhong+python at gmail.com
Wed May 25 05:04:35 EDT 2016
On Wed, May 25, 2016 at 1:52 AM, Nick Coghlan <ncoghlan at gmail.com> wrote:
> Using the running demo:
>
> def demo(arg):
> given arg:
> case x, y, *_: # Tuple matching (implicit name binding)
> ...
> case (.x, .y) as p, q: # Attribute matching
> ...
> case (["x"], ["y"]) as p, q: # Item matching
> ...
> case (.x) as p and isinstance(p, int): # Match + condition
> ...
> case if isinstance(arg, int): # Condition only
> ...
> else: # Default
> ...
>
> The other key change there is introducing "as" to the individual cases
> in order to be able to separate the match pattern definition from the
> local name binding.
I still don't like that `case THING` is a pattern, rather than a value
to test against. Here's my modifications with "as", attributes, and
def demo(arg):
given arg:
case as x, y, *_: # Tuple matching (implicit name binding)
...
case as object(x=p, y=q, **_): # Attribute matching
...
case as {'x': p, 'y', q, **_}: # Item matching
...
case as object(x=p, **_) and isinstance(p, int): # Match + condition
...
case if isinstance(arg, int): # Condition only
...
else: # Default
...
Here, the "as" pattern is a shape to fit the object's parts into, and
it should be possible to get back the original (or something
isomorphic to it) by evaluating the pattern expression (so `case as
{'x': p, 'y', q, **_}": assert isomorphic({'x': p, 'y', q, **_},
arg)`).
For attribute-matching, I think it's possible to make user types also
play well with this syntax for attribute matching, and I made a
proposal earlier for an API. (Section 'Matching user classes' here:
https://mail.python.org/pipermail/python-ideas/2016-May/040343.html)
(Proposal to make the `object` case more reasonable:
`object(**kwargs)` constructor creates an object with the given
attributes. It won't affect subclasses, because `object.__new__` can
test whether `cls is object`.)
Perhaps literal dicts could match against sequences, too.
{0: x, 42: y, **rest} = some_list
And this could be how you enforce types:
dict({0: x, 42: y, **rest}) = some_list # Fails.
tuple((x, y, z)) = some_list # Fails.
though I haven't figured out how these constructor shapes would be
implemented for user types.
Big conceptual obstacle: iterating over a dict gives its keys (which
has always bothered me), so as a value, `list({0: x, 42: y, **rest})`
would just be a list of keys. (I'd give up the typecheck syntax,
personally, and have you move the check into a guard. I'm not too
attached to the indexing syntax, either.)
Note that I force an explicit `**_, so that `object(x=p)` will fail if
`arg` has (non-dunder) attributes other than `x` (which I think is a
good thing). It's kinda wasteful to pack unused things into a
variable, so `...` could specify ignored args (which has come up
before on this list), and the matcher engine can tell the type's
matchmaker that it doesn't care about the other args.
Problem: `Point(x, y, ...)` is a legitimate function call, so if
`Point(x, 0)` is a legal pattern (i.e. no distinguishing syntax
between values and bindnames), you'd need the syntax to be `Point(x,
y, *...)`. Personally, I'd require that everything is a bindname
(unless it looks like a constructor call), and require checks to be in
guards.
More information about the Python-ideas
mailing list