Oscar Benjamin's study of sympy is part of what prompted this, and does provide a concrete example of why constructors should be echoed.
I think in general, the matching has fallen into two categories:
(1) Simple literal-like matching, that mostly works OK. There is still some concern over what is a bind variable vs a match constraint, but it mostly works. And everyone agrees that it isn't the important or interesting part of the proposal.
(2) Object destructuring matches, that ... are not as close to resolution. It occurs to me that object creation is also a function call (albeit with an implicit self), so this may be a good place to build on Bound Signatures. (Think inspect.Parameter, but also containing the value.)
I hope (and think) that the result for sympy would be about what Oscar asked for (below), so I'll fill in with the more generic Point-based example.
class Point: def __init__ Point(self, x, y, z=0, *, color=Color.BLACK): ...
case Point(23, y=y, oldcolor=color): # z doesn't matter
I have weak opinions on whether to require y=y to (or y= or :=y or ...) to capture one of the variables when it isn't being renamed.
Oscar Benjamin wrote:
I've taken a look through PEP 622 and I've been thinking about how it could be used with sympy.
... The key feature of Basic instances is that they have an .args attribute which can be used to rebuild the object ...
All Basic classes are strictly constructed using positional only arguments and not keyword arguments. In the PEP it seems that we can handle positional arguments when their number is fixed by the type. ... The main problem though is with variadic positional arguments. ...
From a first glimpse of the proposal I thought I could do matches like this: match obj: case Add(Mul(x, y), Mul(z, t)) if y == t: case Add(terms): case Mul(coeff, factors): case And(Or(A, B), Or(C, D)) if B == D: case Union(Interval(x1, y1), Interval(x2, y2)) if y1 == x2: case Union(Interval(x, y), FiniteSet(p)) | Union(FiniteSet(p), Interval(x, y)): case Union(*sets): Knowing the sympy codebase each of those patterns would look quite natural because they resemble the constructors for the corresponding objects (as intended in the PEP). It seems instead that many of these constructors would need to have args= so it becomes: match obj: case Add(args=(Mul(args=(x, y)), Mul(args=(z, t)))) if y == t: case Add(args=terms): case Mul(args=(coeff, *factors)): case And(args=(Or(args=(A, B)), Or(args=(C, D)))) if C == D: case Union(args=(Interval(x1, y1), Interval(x2, y2))) if y1 == x2: case Union(args=(Interval(x, y), FiniteSet(args=p))) | Union(args=(FiniteSet(args=p), Interval(x, y))): case Union(args=sets): Each of these looks less natural as they don't match the constructors and the syntax gets messier with nesting.