[Python-Dev] Re: The semantics of pattern matching for Python

2020-11-21 Thread Mark Shannon




On 20/11/2020 6:57 pm, Tobias Kohn wrote:

Hi Daniel and Mark,

Sorry for being slightly late to the party, but please let me add a few 
remarks of my own to the discussion here.


*1. Must have precisely defined semantics*

Yes, there are some aspects that we left open intentionally.  Most


Why? Daniel has written up a formal semantics, why not use that or 
something like it?


prominently the question of how often the pattern matching engine will 
check whether the subject is an instance of a particular class.  Take 
the following trivial example::


   match some_data:
   case Pair(12, 34):
   ...
   case Triple(12, 34, z):
   ...
   case Pair(12, y):
   ...
   case Pair(x, y):
   ...

In a perfect world, the compiler discovers that it must check whether 
``some_data`` is an instance of ``Pair`` exactly once and not three 
times.  This, of course, plays right into Mark's second point on 
efficiency and seems obvious enough.  Yet, as soon as we are considering 
nested patterns, it turns much less obvious whether the compiler is 
supposed to cache repeated isinstance-checks.  Can we really expect that 
the compiler must discover that in both case clauses the first element 
is checked against the same class?  Or would it make more sense to 
simply expect the compiler to potentially perform this ``Num`` instance 
check twice::


   match some_data:
   case [ Num(), 12 ]:
   ...
   case [ Num(), y, *z ]:
   ...

It is easy to think of cases where we accidentally end up calling an 
isinstance check more than once because the compiler could not prove 
that they are equal.  Still, whenever possible we want to give the 
compiler the freedom to optimise the pattern matching statement by caching.


An optimization (in the CS sense, not the math sense) is a 
performance-improving, semantic-preserving transformation.

How can I preserve semantics if you won't tell me what the semantics are?



In a static language, all of this would not be an issue at all, of 
course.  In Python, however, we end up being caught between its dynamic 
features and the desire to make pattern matching reasonably efficient.  
So, we ended up leaving the question open as how often the pattern 
matching engine is allowed or supposed to check instances.  Naturally, 
if you go and write some isinstance-check on a class with side-effects, 
you can break it.



*2. Users should not have to pay an unnecessary performance penalty to 
use pattern matching*


To quote Mark [1] here:

/> Users should not have to pay an unnecessary performance penalty to 
use pattern matching./


Alright, what does this even mean?  What is an unnecessary performance 
penalty?  How should that be measured or compared?


If we shipped Python compiled with -O1, instead of -O3. It would be 
slower and unnecessarily so. That would be an unnecessary performance 
penalty.


In this context, I mean that there is an obvious performance improvement 
available and it hasn't been implemented. Or that the specification 
prevents an optimization without any compensating benefit.


Without a well defined semantics, I can't optimize at all. That is an 
unnecessary performance penalty.




Pattern matching is not just fancy syntax for an if-elif-statement, but 
a new way of writing and expressing structure.  There is currently 
nothing in Python that fully compares to pattern matching (which is 
obviously why we propose to add in the first place).  So, do you want to 
compare a pattern matching structure to an if-elif-chain or rather an 
implementation using reflection and/or the visitor pattern?  When 
implementing pattern matching, would we be allowed to trade off a little 
speed handling the first pattern for moving faster to patterns further down?


Do our PEPs really read to you like we went out of our ways to make it 
slow or inefficient?  Sure, we said let's start with an implementation 
that is correct and worry about optimising it later.  But I thought this 
is 101 of software engineering, anyway, and am thus rather surprised to 
find this item on the list.



*3. Failed matches should not pollute the enclosing namespace*

This is a slightly wider issue that has obviously sparked an entire 
discussion on this mailing list on scopes.


If there is a good solution that only assigns variables once the entire 
pattern matched, I would be very happy with that.  However, I think that 
variables should be assigned in full before evaluating any guards---even 
at the risk of the guard failing and variables being assigned that are 
not used later on.  Anything else would obviously introduce a mini-scope 
and lead to shadowing, which hardly improves anything with respect to 
legibility.


How would it introduce shadowing? Since `var` is a capture pattern, it 
can't read and thus can't be shadowed.





*4. Objects should be able determine which patterns they match*

Short version: no!


Yes! ;)



Class patter

[Python-Dev] Re: The semantics of pattern matching for Python

2020-11-21 Thread Steven D'Aprano
On Fri, Nov 20, 2020 at 02:23:45PM +, Mark Shannon wrote:

> Why force pattern matching onto library code that was not designed for 
> pattern matching? It seems risky.

Can you give a concrete example of how this will "force" pattern 
matching onto library code? I don't think that anyone has suggested that 
we go around to third-party libraries and insert pattern matching in 
them, so I'm having trouble understanding your fear here.



> Fishing arbitrary attributes out of an object and assuming that the 
> values returned by attribute lookup are equivalent to the internal 
> structure breaks abstraction and data-hiding.

Again, can we have a concrete example of what you fear?

Python is not really big on data-hiding. It's pretty close to impossible 
to hide data in anything written in pure Python.



> An object's API may consist of methods only. Pulling arbitrary 
> attributes out of that object may have all sorts of unintended side-effects.

Well, sure, but merely calling print() on an object might have all sorts 
of unintended side-effects. I think that almost the only operation 
guaranteed to be provably side-effect free in Python is the `is` 
operator. So I'm not sure what you fear here?

If I have a case like:

match obj:
case Spam(eggs=x):

I presume any sensible implementation is going to short-cut the 
attempted pattern match for x if obj is *not* an instance of Spam. So 
it's not going to be attempting to pull out arbitrary attributes of 
arbitrary objects, but only specific attributes of Spam objects.

To the degree that your objection here has any validity at all, surely 
it has been true since Python 1.5 or older that we can pull arbitrary 
attributes out of unknown objects? That's what duck-typing does, whether 
you guard it with a LBYL call to hasattr or an EAFP try...except block.

if hasattr(obj, 'eggs'):
result = obj.eggs + 1

Not only could obj.eggs have side-effects, but so could the call to 
hasattr. Can you explain how pattern matching is worse than what we 
already do?


> PEP 634 and the DLS paper assert that deconstruction, by accessing 
> attributes of an object, is the opposite of construction.
> This assertion seems false in OOP.


Okay. Does it matter?

Clearly Spam(a=1, b=2) does not necessarily result in an instance with 
attributes a and b. But the pattern `Spam(a=1, b=2)` is intended to be 
equivalent to (roughly):

if (instance(obj, Spam)
and getattr(obj, a) == 1
and getattr(obj, b) == 2)

it doesn't imply that obj was *literally* created by a call to 
the constructor `Spam(a=1, b=2)`, or even that this call would be 
possible.

I think that it will certainly be true that for many objects, there is a 
very close (possibly even exact) correspondence between the constructor 
parameters and the instance attributes, i.e. deconstruction via 
attribute access is the opposite of construction.

But for the exceptions, why does it matter that they are exceptions?

Let me be concrete for the sake of those who may not be following these 
abstract arguments. Suppose I have a class:


class Car:
def __init__(self, brand, model):
self.brand = brand
self.model = model


and an instance:


obj = Car("Suzuki", "Swift")


For this class, deconstruction by attribute access is exactly the 
opposite of construction, and I can match any Suzuki like this:


match obj:
case Car(brand="Suzuki", model)


which is roughly equivalent to:


if isinstance(obj, Car) and getattr(obj, "brand") == "Suzuki":
model = getattr(obj, "model")


It's not actually asserting that the instance *was* constructed with a 
call to `Car(brand="Suzuki", model="Swift")`, only that for the purposes 
of deconstruction it might as well have been.

If the constructor changes, leaving the internal structure the same:


class Car:
def __init__(self, manufacturer, variant):
self.brand = manufacturer
self.model = variant


the case statement need not change.

Remember that non-underscore attributes are public in Python, so a 
change to the internal structure:


class Car:
def __init__(self, brand, model):
self.brand_name = brand
self.model_id = model


is already a breaking change, whether we have pattern matching or not.


> When we added the "with" statement, there was no attempt to force 
> existing code to support it. We made the standard library support it, 
> and let the community add support as and when it suited them.
>
> We should do the same with pattern matching.

That's a terrible analogy. Pattern matching is sugar for things that we 
can already do:

- isinstance
- getattr
- sequence unpacking
- equality
- dict key testing

etc. Pattern matching doesn't "force" objects to support anything they 
don't already support.

As far as I can tell, the only thing that the community will need to add 
support for is the mapping between positional

[Python-Dev] Re: The semantics of pattern matching for Python

2020-11-21 Thread David Mertz
On Sat, Nov 21, 2020 at 12:23 PM Steven D'Aprano 
wrote:

> Clearly Spam(a=1, b=2) does not necessarily result in an instance with
> attributes a and b. But the pattern `Spam(a=1, b=2)` is intended to be
> equivalent to (roughly):
>
> if (instance(obj, Spam)
> and getattr(obj, a) == 1
> and getattr(obj, b) == 2)
>
> it doesn't imply that obj was *literally* created by a call to
> the constructor `Spam(a=1, b=2)`, or even that this call would be
> possible.
>

I think this explanation makes me not worry about the fact that `Spam(a=1,
b=2)` in a pattern looks a lot like a constructor.  Like some other
commenters, I was vaguely bothered that the identical spelling might have
these different meanings in different contexts.  But I think a match case
just clearly enough IS a different context that using slightly different
intuitions is no real conceptual stretch for remembering or teaching it.

As a strawman, we could use different syntax for "match the thing of class
Spam that has attributes with these values:

match eggs:
case Spam[a=1, b=2]: ...

Or:

match eggs:
case Spam{a=1, b=2}: ...

Well, the square brackets COULD mean something different if PEP 637 is
adopted.  But even supposing the curly braces could be fit into the
grammar.  Yes, it sort of suggests the connection between dictionaries and
Spam.__dict__.  But it still reads as "this is something special that I
have to think about a little differently."

Even where there are capture variables, I think I'd be completely
comfortable thinking about the different context for:

match eggs:
case Spam(a=x, b=2): ...

-- 
The dead increasingly dominate and strangle both the living and the
not-yet born.  Vampiric capital and undead corporate persons abuse
the lives and control the thoughts of homo faber. Ideas, once born,
become abortifacients against new conceptions.
___
Python-Dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/[email protected]/message/XSRVX2NTDGUF7CWTPR5SHIOAQPWNXXYZ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: The semantics of pattern matching for Python

2020-11-21 Thread Andrew Svetlov
If you don't want the overlapping with the existing syntax angle brackets
can be used:

match eggs:
case Spam: ...


On Sat, Nov 21, 2020 at 7:31 PM David Mertz  wrote:

> On Sat, Nov 21, 2020 at 12:23 PM Steven D'Aprano 
> wrote:
>
>> Clearly Spam(a=1, b=2) does not necessarily result in an instance with
>> attributes a and b. But the pattern `Spam(a=1, b=2)` is intended to be
>> equivalent to (roughly):
>>
>> if (instance(obj, Spam)
>> and getattr(obj, a) == 1
>> and getattr(obj, b) == 2)
>>
>> it doesn't imply that obj was *literally* created by a call to
>> the constructor `Spam(a=1, b=2)`, or even that this call would be
>> possible.
>>
>
> I think this explanation makes me not worry about the fact that `Spam(a=1,
> b=2)` in a pattern looks a lot like a constructor.  Like some other
> commenters, I was vaguely bothered that the identical spelling might have
> these different meanings in different contexts.  But I think a match case
> just clearly enough IS a different context that using slightly different
> intuitions is no real conceptual stretch for remembering or teaching it.
>
> As a strawman, we could use different syntax for "match the thing of class
> Spam that has attributes with these values:
>
> match eggs:
> case Spam[a=1, b=2]: ...
>
> Or:
>
> match eggs:
> case Spam{a=1, b=2}: ...
>
> Well, the square brackets COULD mean something different if PEP 637 is
> adopted.  But even supposing the curly braces could be fit into the
> grammar.  Yes, it sort of suggests the connection between dictionaries and
> Spam.__dict__.  But it still reads as "this is something special that I
> have to think about a little differently."
>
> Even where there are capture variables, I think I'd be completely
> comfortable thinking about the different context for:
>
> match eggs:
> case Spam(a=x, b=2): ...
>
> --
> The dead increasingly dominate and strangle both the living and the
> not-yet born.  Vampiric capital and undead corporate persons abuse
> the lives and control the thoughts of homo faber. Ideas, once born,
> become abortifacients against new conceptions.
> ___
> Python-Dev mailing list -- [email protected]
> To unsubscribe send an email to [email protected]
> https://mail.python.org/mailman3/lists/python-dev.python.org/
> Message archived at
> https://mail.python.org/archives/list/[email protected]/message/XSRVX2NTDGUF7CWTPR5SHIOAQPWNXXYZ/
> Code of Conduct: http://python.org/psf/codeofconduct/
>


-- 
Thanks,
Andrew Svetlov
___
Python-Dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/[email protected]/message/45JLJCSQRZ3SCVV6EP546W4P5LBYNUKM/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Words rather than sigils in Structural Pattern Matching

2020-11-21 Thread David Mertz
I'm not sure it matters in all the threads.  But my underlying opinion is
that we SHOULD have a mechanism to use a plain name (that has an assigned
value in the scope) as a value in match patterns, not only as binding
targets.

However, in contrast to Nick Coghlan, I do not think access should be the
default, but that binding should be.  I'm convinced by Guido, Brandt, and
others that the binding  use will be far more common, so adding extra
characters for the 90% case does not feel desirable, even if it is arguably
more consistent with other elements of the language.

The second example I give of binding requiring the extra syntax was for
completeness, but I would not support it myself.  If binding were to
require extra syntax, I think Nick's 'as' suggestion is better than my
prefix word idea (if only because it doesn't need a new soft keyword).  I
do not think the answer of "put the plain name into an enumeration or other
object so it can be accessed as a dotted name" is quite enough.  Yes,
that's possible, but it feels more forced.

So in my mind, if I had the choice, it is a decision between a sigil and a
word to indicate "no, really use this name as a value!" I like a word
better, but none of the current keywords really make sense, so it would
need to be a new word. I suggested "value", but another word might be
better.

On Thu, Nov 12, 2020 at 7:38 PM David Mertz  wrote:

> One idea that I cannot recall seeing, but that seems to make sense to me
> and fit with Python's feel is using a WORD to distinguish between a
> variable value and a binding target.  That is, instead of a special symbol
> prefixing or suffixing a name, either to indicate it is or is not a binding
> target.  Of course, whether the extra word would be used for binding or for
> NOT binding is a question still.
>
> NOT_FOUND = 404
> match http_code:
> case 200:
> print("OK document")
> case value NOT_FOUND:  # use the variable value
> print("Document not found")
> case OTHER_CODE:  # bind this name
> print("Other HTTP code", OTHER_CODE)
>
> Of course, this would require a soft keyword, which is a disadvantage.
> Going the other direction:
>
> NOT_FOUND = 404
> match http_code:
> case 200:
> print("OK document")
> case NOT_FOUND:  # use the variable value
> print("Document not found")
> case bind OTHER_CODE:  # bind this name
> print("Other HTTP code")
>
> To me these read better than the punctuation characters.  But I guess some
> folks have suggested enlisting 'as', which is a word, of course.
>
>
>
>
> --
> Keeping medicines from the bloodstreams of the sick; food
> from the bellies of the hungry; books from the hands of the
> uneducated; technology from the underdeveloped; and putting
> advocates of freedom in prisons.  Intellectual property is
> to the 21st century what the slave trade was to the 16th.
>


-- 
The dead increasingly dominate and strangle both the living and the
not-yet born.  Vampiric capital and undead corporate persons abuse
the lives and control the thoughts of homo faber. Ideas, once born,
become abortifacients against new conceptions.
___
Python-Dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/[email protected]/message/WAGBJUKGS5V7AGUYDWNZAI5NNA76MXYN/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Words rather than sigils in Structural Pattern Matching

2020-11-21 Thread Guido van Rossum
On Sat, Nov 21, 2020 at 9:52 AM David Mertz  wrote:

> So in my mind, if I had the choice, it is a decision between a sigil and a
> word to indicate "no, really use this name as a value!" I like a word
> better, but none of the current keywords really make sense, so it would
> need to be a new word. I suggested "value", but another word might be
> better.
>

The good news here is that the PEP 634 syntax is sufficiently restricted
that we can always add this later, regardless of whether we end up wanting
a sigil or a (soft) keyword, as long as we agree that the meaning of a bare
name should be a capture variable.

-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*

___
Python-Dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/[email protected]/message/2ZME3J6XZYHCAYEKFC6OJ7LKA7TFRI7P/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Words rather than sigils in Structural Pattern Matching

2020-11-21 Thread David Mertz
On Sat, Nov 21, 2020 at 2:47 PM Guido van Rossum  wrote:

> On Sat, Nov 21, 2020 at 9:52 AM David Mertz  wrote:
>
>> So in my mind, if I had the choice, it is a decision between a sigil and
>> a word to indicate "no, really use this name as a value!" I like a word
>> better, but none of the current keywords really make sense, so it would
>> need to be a new word. I suggested "value", but another word might be
>> better.
>>
>
> The good news here is that the PEP 634 syntax is sufficiently restricted
> that we can always add this later, regardless of whether we end up wanting
> a sigil or a (soft) keyword, as long as we agree that the meaning of a bare
> name should be a capture variable.
>

I thought of that.  An incremental approach of accepting PEP 634, but
(perhaps) adding a soft keyword later is probably the most sensible thing.

If the "need the value of a plain name" is a 1% case, a workaround like
putting it into an attribute of some object before matching is a minor
issue.  If that's the 25%+ case, after experience, the argument for some
annotation of "use the value" could be added later.


-- 
The dead increasingly dominate and strangle both the living and the
not-yet born.  Vampiric capital and undead corporate persons abuse
the lives and control the thoughts of homo faber. Ideas, once born,
become abortifacients against new conceptions.
___
Python-Dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/[email protected]/message/Q7NUDCGLH2OCRK5XJ5QEEOIZ4FECKPGO/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: The semantics of pattern matching for Python

2020-11-21 Thread Guido van Rossum
On Mon, Nov 16, 2020 at 6:41 AM Mark Shannon  wrote:

> I believe that a pattern matching implementation must have the following
> properties:
>
> * The semantics must be precisely defined.
> * It must be implemented efficiently.
> * Failed matches must not pollute the enclosing namespace.
> * Objects should be able determine which patterns they match.
> * It should be able to handle erroneous patterns, beyond just syntax
> errors.
>
> PEP 634 and PEP 642 don't have *any* of these properties.
>

Mark, I trust that those are your beliefs. But your words are just
sufficiently vague that anyone can read into them what they want. (IOW, the
semantics of your words are not precisely defined. :-)

1.
When drafting PEP 634,  I spent a *lot* of time making the description of
the semantics as clear and precise as they could be, using carefully
defined terms like matching, subpattern, binding and so on. The syntax of
patterns is defined recursively, and so are the semantics. If you have
specific concerns about ambiguities in the description, please quote the
relevant text of the PEP and ask what the intention is or just suggest an
improvement. Otherwise I cannot do anything with your claim "the PEP does
not describe the semantics precisely" except disagreeing. Note that
specifying semantics in terms of a translation to "equivalent" Python code
is also fraught with difficulties, and is in danger of over-specifying
irrelevant details.

2.
Regarding efficient implementation, I would phrase it differently. I think
that it must be *possible* to implement it efficiently. During the design
phase we spent a lot of time thinking about the implementation, and the
specification leaves plenty of room for improving upon Brandt's initial
implementation. Your demand that the initial implementation be "efficient"
is baseless, and your claim that the reference implementation isn't
sufficiently efficient is insulting to Brandt. I also see no reason to
believe it. Historically, everything in Python starts out relatively
inefficient and over time we speed up those parts of the implementation
that need it.

3.
Your claim "Failed matches must not pollute the enclosing namespace" is
entirely subjective. Since succeeding matches store captured values in the
enclosing namespace, those variables that may be overwritten by the
matching process are liable to be overwritten anyway. I've seen your
counter-proposals for this (both save/restore and $-number variables), and
I won't object if you implement something like that after the initial
implementation lands, but I *would* object if I couldn't enter e.g. set a
breakpoint on a guard expression in a debugger and inspect the capture
variables using their regular names. I don't see your statement as a
requirement for an initial implementation, and I think the compiler will be
simpler without it.

4.
"Objects should be able [to] determine which patterns they match" is again
a subjective claim. We debated adding a `__match__` method to objects to
enable this but couldn't get agreement on how it should be called and what
it should return. We even came up with a proposal that made it into the
first draft of PEP 622, after a long debate about the efficiency of the
interface. But in the end it added complexity and we didn't have any
sufficiently strong use cases, so we decided that we would punt on this
one. This is the kind of thing that can easily be added in a future
revision without breaking backwards compatibility, so we took it out to
limit the scope of the proposal.

5.
Your bullet about erroneous patterns seems to be meant to call out one
particular quirk of the proposal, which is that class patterns can specify
as keywords anything that is a valid attribute, which includes for example
methods. My attitude towards this can be characterized as "so what?" It has
already been pointed out that the same mistake can occur in
non-pattern-matching code (e.g. `if nt.count == 1`), and I don't think this
kind of mistake is going to be any more prevalent in class patterns than it
is in other code.

Finally, recalling your past comments on PEP 622, PEP 634, and other PEPs,
I feel hurt by the apparent disrespect you express for the authors and
their designs (the current thread being just one example). If I compare
Larry Hastings' post to discourse

where he explains that he doesn't want pattern matching I see none of that,
but in your posts it shines through again and again. Can I ask you to
please take a step back and think about why this may be, and try to change
something in your writing style? We all will need to get along for a long
time after this particular topic is put to rest, but each time I see one of
your posts like this I find it harder to respond rationally rather than
emotionally. (It is no accident that it took me 5 days to respond here.)

-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun

[Python-Dev] Re: Words rather than sigils in Structural Pattern Matching

2020-11-21 Thread Glenn Linderman

On 11/21/2020 9:47 AM, David Mertz wrote:
So in my mind, if I had the choice, it is a decision between a sigil 
and a word to indicate "no, really use this name as a value!" I like a 
word better, but none of the current keywords really make sense, so it 
would need to be a new word. I suggested "value", but another word 
might be better.


NOT_FOUND = 404
match var:
    case 200:
    print("OK")
    case (NOT_FOUND):
    print("Not Found")
    case other_code:
    print("Other code", other_code )

Don't () already indicate an expression to be evaluated?

It's only one character longer than the early . suggestion, and already 
has the desired meaning of "calculated value" not "bound variable".

___
Python-Dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/[email protected]/message/EQ2DSQ3Y4FBZQGW36NKCAAABOUSPZVWH/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: The semantics of pattern matching for Python

2020-11-21 Thread Tobias Kohn

 Hi Steve,

Thank you very much for your comments here.  This is certainly not the  
first time I feel that you not only have an excellent insight into a  
topic, but also manage to make your points very clearly and  
succinctly.  Your car example highlights the background of the  
proposed syntax very nicely, indeed, and the for-in example strikes me  
as quite astute.


Kind regards,
Tobias

Quoting Steven D'Aprano :


On Fri, Nov 20, 2020 at 02:23:45PM +, Mark Shannon wrote:


Why force pattern matching onto library code that was not designed for
pattern matching? It seems risky.


Can you give a concrete example of how this will "force" pattern
matching onto library code? I don't think that anyone has suggested that
we go around to third-party libraries and insert pattern matching in
them, so I'm having trouble understanding your fear here.


Fishing arbitrary attributes out of an object and assuming that the
values returned by attribute lookup are equivalent to the internal
structure breaks abstraction and data-hiding.


Again, can we have a concrete example of what you fear?

Python is not really big on data-hiding. It's pretty close to impossible
to hide data in anything written in pure Python.


An object's API may consist of methods only. Pulling arbitrary
attributes out of that object may have all sorts of unintended side-effects.


Well, sure, but merely calling print() on an object might have all sorts
of unintended side-effects. I think that almost the only operation
guaranteed to be provably side-effect free in Python is the `is`
operator. So I'm not sure what you fear here?

If I have a case like:

   match obj:
       case Spam(eggs=x):

I presume any sensible implementation is going to short-cut the
attempted pattern match for x if obj is *not* an instance of Spam. So
it's not going to be attempting to pull out arbitrary attributes of
arbitrary objects, but only specific attributes of Spam objects.

To the degree that your objection here has any validity at all, surely
it has been true since Python 1.5 or older that we can pull arbitrary
attributes out of unknown objects? That's what duck-typing does, whether
you guard it with a LBYL call to hasattr or an EAFP try...except block.

   if hasattr(obj, 'eggs'):
       result = obj.eggs + 1

Not only could obj.eggs have side-effects, but so could the call to
hasattr. Can you explain how pattern matching is worse than what we
already do?


PEP 634 and the DLS paper assert that deconstruction, by accessing
attributes of an object, is the opposite of construction.
This assertion seems false in OOP.


Okay. Does it matter?

Clearly Spam(a=1, b=2) does not necessarily result in an instance with
attributes a and b. But the pattern `Spam(a=1, b=2)` is intended to be
equivalent to (roughly):

   if (instance(obj, Spam)
       and getattr(obj, a) == 1
       and getattr(obj, b) == 2)

it doesn't imply that obj was *literally* created by a call to
the constructor `Spam(a=1, b=2)`, or even that this call would be
possible.

I think that it will certainly be true that for many objects, there is a
very close (possibly even exact) correspondence between the constructor
parameters and the instance attributes, i.e. deconstruction via
attribute access is the opposite of construction.

But for the exceptions, why does it matter that they are exceptions?

Let me be concrete for the sake of those who may not be following these
abstract arguments. Suppose I have a class:

   class Car:
       def __init__(self, brand, model):
           self.brand = brand
           self.model = model

and an instance:

   obj = Car("Suzuki", "Swift")

For this class, deconstruction by attribute access is exactly the
opposite of construction, and I can match any Suzuki like this:

   match obj:
       case Car(brand="Suzuki", model)

which is roughly equivalent to:

   if isinstance(obj, Car) and getattr(obj, "brand") == "Suzuki":
       model = getattr(obj, "model")

It's not actually asserting that the instance *was* constructed with a
call to `Car(brand="Suzuki", model="Swift")`, only that for the purposes
of deconstruction it might as well have been.

If the constructor changes, leaving the internal structure the same:

   class Car:
       def __init__(self, manufacturer, variant):
           self.brand = manufacturer
           self.model = variant

the case statement need not change.

Remember that non-underscore attributes are public in Python, so a
change to the internal structure:

   class Car:
       def __init__(self, brand, model):
           self.brand_name = brand
           self.model_id = model

is already a breaking change, whether we have pattern matching or not.


When we added the "with" statement, there was no attempt to force
existing code to support it. We made the standard library support it,
and let the community add support as and when it suited them.

We should do the same with pattern matching.


That's a terrible analogy. Pattern matching is sug

[Python-Dev] Re: Words rather than sigils in Structural Pattern Matching

2020-11-21 Thread Marco Sulla
On Sat, 21 Nov 2020 at 18:52, David Mertz  wrote:
> So in my mind, if I had the choice, it is a decision between a sigil and a 
> word
> to indicate "no, really use this name as a value!" I like a word better, but 
> none
> of the current keywords really make sense, so it would need to be a new word.
> I suggested "value", but another word might be better.

What about `equals`?
___
Python-Dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/[email protected]/message/PFQJN4QY33QUQVSNOB55VTMLNHJZBCWN/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: The semantics of pattern matching for Python

2020-11-21 Thread Tobias Kohn

 Hi David and Steve,

There is hardly anything that needs to be added to your comments, of  
course.  However, I consider these explicitly named attributes in the  
class pattern to be one of the most difficult aspects of our pattern  
matching proposal, which is something I just want to briefly  
acknowledge.  Although there are good reasons for using the syntax as  
proposed, understanding that ``Spam(a=x)`` assigns the attribute ``a``  
to the variable ``x`` is not quite as intuitive and straight forward.


Using the curly braces for that purpose might help in that we  
instantly think differently and more in line with dictionaries. This  
small clue could potentially have quite an impact on readability.   
This idea has thus a huge advantage over the square brackets not  
because of PEP 637, but because it might change the way we look at the  
code at hand.


The reason why I would not favour this specific syntax is because we  
expect that the predominant case would be to just write ``Spam(1, 2)``  
without the attribute names.  It is even possible to mix and match the  
two, which would then lead to something like ``Spam{1, b=2}``.  In  
that case, the advantage of the dictionary-like notation might just  
evaporate I assume, leaving behind a notation that turns out to be  
unusual and somewhat annoying in the overwhelming majority of cases.


There are a few other concepts and ideas behind it all, which I do not  
want to go into unless there is demand for it, but part of it is the  
idea that we want to be as much as possible in line with the syntax  
already used in iterable unpacking, say.


Kind regards,
Tobias

Quoting David Mertz :


On Sat, Nov 21, 2020 at 12:23 PM Steven D'Aprano  wrote:


Clearly Spam(a=1, b=2) does not necessarily result in an instance with
attributes a and b. But the pattern `Spam(a=1, b=2)` is intended to be
equivalent to (roughly):

    if (instance(obj, Spam)
        and getattr(obj, a) == 1
        and getattr(obj, b) == 2)

it doesn't imply that obj was *literally* created by a call to
the constructor `Spam(a=1, b=2)`, or even that this call would be
possible.


 
I think this explanation makes me not worry about the fact that  
`Spam(a=1, b=2)` in a pattern looks a lot like a constructor.  Like  
some other commenters, I was vaguely bothered that the identical  
spelling might have these different meanings in different contexts.   
But I think a match case just clearly enough IS a different context  
that using slightly different intuitions is no real conceptual  
stretch for remembering or teaching it.

 
As a strawman, we could use different syntax for "match the  
thing of class Spam that has attributes with these values:

 
match eggs:
    case Spam[a=1, b=2]: ...
 
Or:
 
match eggs:
    case Spam{a=1, b=2}: ...
 
Well, the square brackets COULD mean something different if PEP  
637 is adopted.  But even supposing the curly braces could be fit  
into the grammar.  Yes, it sort of suggests the connection between  
dictionaries and Spam.__dict__.  But it still reads as "this is  
something special that I have to think about a little differently."

 
Even where there are capture variables, I think I'd be  
completely comfortable thinking about the different context for:

 
match eggs:
    case Spam(a=x, b=2): ...
 

--
   The dead increasingly dominate and strangle both the living and the
not-yet born.  Vampiric capital and undead corporate persons abuse
the lives and control the thoughts of homo faber. Ideas, once born,
become abortifacients against new conceptions.
___
Python-Dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/[email protected]/message/J4GVSQN4UPOCN3EXZASQOIWN7MO723A4/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Words rather than sigils in Structural Pattern Matching

2020-11-21 Thread Henk-Jaap Wagenaar
On Sat, 21 Nov 2020 at 19:58, Glenn Linderman  wrote:

> Don't () already indicate an expression to be evaluated?
>

Does it?

[(a, b)] = [(0, 1)]
print(a)

gives 0 as output.
___
Python-Dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/[email protected]/message/2HUHO5RB3UKNCHVVX6AAY6ZS25DSNCTL/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Words rather than sigils in Structural Pattern Matching

2020-11-21 Thread Greg Ewing

On 22/11/20 6:47 am, David Mertz wrote:
I'm convinced by Guido, 
Brandt, and others that the binding  use will be far more common, so 
adding extra characters for the 90% case does not feel desirable


Minimising the number of characters is not the only consideration.
Readability counts too, and I think the proposed DWIM rules suffer
in the readability area.

There are about five different contexts in which a bare name can
appear as part of a match case:

* As a constructor name
* As a bare name in an argument position
* As part of a dotted expression
* On the left of an =
* On the right of an =

Only in some of those contexts is it treated as a name to be
assigned. That's a fairly complex bit of mental parsing to do
when reading a case.

--
Greg
___
Python-Dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/[email protected]/message/LMALNSPBLL3MHFOJHBGGO24IDS6CI5R3/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Words rather than sigils in Structural Pattern Matching

2020-11-21 Thread Greg Ewing

On 22/11/20 1:07 pm, Henk-Jaap Wagenaar wrote:
On Sat, 21 Nov 2020 at 19:58, Glenn Linderman > wrote:


Don't () already indicate an expression to be evaluated?

Does it?

[(a, b)] = [(0, 1)]


Presumably a comma would be needed to match a 1-tuple.

   case (x): # matches the value of x

   case (x,): # matches any 1-tuple and binds x

--
Greg
___
Python-Dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/[email protected]/message/4T3OFJEIGMZEW5QJ3WXFAICK6LZD3P5Y/
Code of Conduct: http://python.org/psf/codeofconduct/