This repository has been archived by the owner on Nov 21, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 65
An alternative '__match__' protocol can be better in simplicity, efficiency, and expressivity #100
Comments
Default Implementation of
|
thautwarm
added a commit
to thautwarm/cpython
that referenced
this issue
Jun 25, 2020
1. interpreter part: add 'MATCH_CALL' instruction to use protocol by gvanrossum/patma#100 * stack effect of 'MATCH_CALL' is -3. 2. compiler part: 'C(arg1, ..., argm, kw1=p1, ..., kwn=pn)' emits <compile(C)> LOAD_CONST m LOAD_CONST ("kw1", ..., "kwn") MATCH_CALL 3. Syntax part: avoid adding dedicated pattern grammar: "case" pattern=star_expressions guard=guard? ':' body=block
This is now implemented in thautwarm/patma. |
class X:
@classmethod
def __match__(self, inst, argc, kwargs):
return inst, argc, kwargs
match (1, 2):
case X(!a, !b, !c), 2:
print(a, b, c)
1 3 () I temporarily use |
This exact proposal was considered and eventually rejected. Please search the tracker. |
I still think this is negotiable because I find the reason you reject this can be resolved by providing a pattern matching utility library. However I see this is not the current focus and discussions about this can be harmful in this stage. I won't talk about this any more unless higher priority issues got addressed. |
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Guido said my blog post is too long, that's because my proposal for an alternative
__match__
protocol took most of the pages...Let me firstly introduce the alternative
__match__
protocol, by showing how it works in thematch
statements.RULE: no args
match v: case C()
matches, iffC.__match__(v, 0, ()) is ()
.RULE: only positional args
match v: case C(a1, a2, ..., an)
matches, iffC.__match__(v, n, ())
matches a sequence pattern(a1, ..., an)
.RULE: only keyword args
match v: case C(k1=kp1, ..., kn=kpn)
matches, iffC.__match__(v, 0, ('k1', ..., 'kn'))
matches a tuple pattern(kp1, ..., kpn)
.RULE: positional keyword
match v: case C(a1, ..., am, k1=kp1, ..., kn=kpn)
matches, iffC.__match__(v, m, ('k1', ..., 'kn'))
matches a tuple pattern(a1, ..., am, kp1, ..., kpn)
In fact, above 4 rules are consistent, and can be unified into one.
I separated them into 4, to show this is FULL-FEATURED, which means it can cover the functionalities of the
__match__
protocol presented in PEP 622.The advantage of this alternative design will make the custom patterns
Simplicity of the alternative
__match__
protocolAvoiding
__match_args__
already brings simplicity.Besides, as the PEP says,
InRange
shall be made with custom patterns,we could consider its implementation.
After reading the PEP, I got to know how to implement this:
However, with the protocol proposed in this article:
Works exactly the same.
We will get the following benefits:
__match_args__
, and its relatively complex rules for__match__
.__match__
has a shape quite similar to the pattern(intuitive).The alternative
__match__
protocol is efficientUsing a proxy type can result in more allocations, but this is not the most severe drawbacks.
The most crucial 2 drawbacks of current
__match__
protocol are:__match_args__
produces an indirect lookup.For the first point:
Now, tell me, what if destructuring
C1
should only accept keyword argumentsa
andb
?Currently, should we have to compute the matching for
C2(...)
,C3(...)
, then findc=C4(...)
invalid.This is inefficient.
Also, when it comes to the positional arguments, currently we can limitedly check if the argument count is valid, by checking the count of sub-patterns with
len(__match_args__)
. However our proposal__match__
protocol allows more flexible checks. See Better Expressivity for more details.Anyway, with our proposal
__match__
protocol, we can fail a match in proper time, to avoid redundant computations.For the second point, indirect lookups can be expensive for tiny operations:
With compiler tricks, we can make
getattr(s, s._kws[1])
faster, but it shall be still far slower thanx[1]
.Drawbacks of this proposal: when a proxy type is not needed, this alternative
__match__
may be slower, as a tuple is always allocated.The alternative
__match__
protocol results in better expressivitySo far, the design for patterns
[]
is generic, which meansSequence
instead oflist
, which can be non-intuitive in some degree.With this alternative
__match__
protocol,*
in class pattern is automatically supported:Like
Sequence(a1, a2, *aseq, an)
, we need to do nothing other than implementing tuple patterns and this__match__
protocol.This is sufficient to support
Sequence
interface, and can be implemented with our proposal__match__
protocol.It's considered useful to support arbitrary number of positional arguments for our custom patterns:
Sequence
andMapping
.[*_]
) as-is, I mean this should only matches alist
instead of anySequence
s.The text was updated successfully, but these errors were encountered: