[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