[Python-Dev] Re: Pattern matching reborn: PEP 622 is dead, long live PEP 634, 635, 636

2020-11-02 Thread Brett Cannon
On Fri, Oct 30, 2020 at 6:39 PM Tin Tvrtković  wrote:

> A small update on this, since I've been playing with it.
>
> I'm trying to implement a websocket proxy, since it's an example of a toy
> project that needs to juggle two long-lived asyncio connections at once.
> I'm using Starlette/Uvicorn for the server part (the part that accepts the
> connection) and aiohttp's client functionality to connect to an echo server
> on the Internet.
>
> I've settled on the async iterator interface as a good building block for
> this, not queues. Turns out an async iterator looks very much as the read
> interface for a Go channel (they spit out values and signal their closure).
> Aiohttp already provides an async iterator interface for reading from the
> upstream server. Starlette doesn't, but an adapter is very easy to write.
>
> Now that we've settled on a suitable interface, a helper function
> (wait_on) is easy to write. (Code omitted for brevity.) This is the entire
> request handler:
>
> async def ws_proxy(client: WebSocket):
> await client.accept()
> async with ClientSession() as session:
> async with session.ws_connect("wss://echo.websocket.org") as s:
> c = starlette_websocket_iterator(client)
> async for r in wait_on(c, s):
> match r:
> case (src, None):
> print(f"{src} closed the connection")
> break
> case (src, msg) if src is c:
> print(f"CLIENT: {msg}")
> await s.send_str(msg)
> case (src, msg) if src is s:
> print(f"SERVER: {msg}")
> await client.send_text(msg.data)
>

To compare against the non-match approach:

async for src, msg in wait_on(c, s):
if msg is None:
print(f"{src} closed the connection")
break
elif src is c:
print(f"CLIENT: {msg}")
await s.send_str(msg)
elif src is s:
print(f"SERVER: {msg}")
await client.send_text(msg.data)

-Brett


>
> So yeah, the helper yields tuples of the source and message, using None as
> a sentinel for closure. Guards are used to match on the source, using
> iterator identity. My first version just used `case (s, msg):` hoping to
> match on the identity of s, but that doesn't work since s is not a literal.
>
> I'd say this is pretty cool. With the ecosystem moving to async iterators
> for streams of data (or just writing adapters), I'd say this style of
> programming is quite readable and understandable.
>
> On Tue, Oct 27, 2020 at 1:16 AM Tin Tvrtković 
> wrote:
>
>> Hello,
>>
>> Go channels are indeed very similar to asyncio Queues, with some added
>> features like channels being closable. (There is also special syntax in the
>> select statement, `val, ok <- chan`, that will set the `ok` variable to
>> false if the channel has been closed.) A larger difference, I think, is
>> that in Go channels are used practically everywhere, more so than asyncio
>> Queues. They are an abstraction the vast majority of Go concurrency is
>> built upon.
>>
>> Building this for asyncio tasks, instead of just queues, would be much
>> more useful in Python.
>>
>> Contemplating this some more, I would agree we don't need an async match.
>> A function and some types to match on would probably be enough to get us
>> close to a select statement in a PEP634 Python. I guess the challenge is
>> designing these matchable types for ease of use now, and I need to study
>> the pattern matching PEPs in more detail to be able to contribute here.
>>
>> On one hand, this means this problem can be solved by a third party
>> library. On the other hand, I feel like this would be very useful so it
>> might be worth it to have it somewhere in the stdlib asyncio namespace.
>>
>> Since `asyncio.wait` can yield multiple tasks in the completed set, this
>> would probably have to be wrapped in an `async for`.
>>
>>
>>
>> On Mon, Oct 26, 2020 at 12:33 PM Gustavo Carneiro 
>> wrote:
>>
>>> It's true that asyncio.wait provides the tools that you need, but it's a
>>> bit clunky to use correctly.
>>>
>>> Maybe would be something along the lines of:
>>>
>>> --
>>> queue1 = asyncio.Queue()
>>> queue2 = asyncio.Queue()
>>> ...
>>> get1 = asyncio.create_task(queue1.get())
>>> get2 = asyncio.create_task(queue2.get())
>>> await asyncio.wait({get1, get2}, return_when=asyncio.FIRST_COMPLETED)
>>> match [task.done() for task in (get1, get2)]:
>>> case [True, False]:  get2.cancel(); item1 = await get1; 
>>> case [False, True]:  get1.cancel(); item2 = await get2; 
>>> case [True, True]:  item1 = await get1; ; item2 = await get2;
>>> 
>>> --
>>>
>>> If asyncio.Queue() is the equivalent of Go channels, perhaps it would be
>>> worth designing a new API for asyncio.Queue, one that is better suited to
>>> the match statement:
>>>
>>> class Queue:
>>>async def read_wait(self) 

[Python-Dev] Re: Pattern matching reborn: PEP 622 is dead, long live PEP 634, 635, 636

2020-10-31 Thread Paul Sokolovsky
Hello,

On Sat, 31 Oct 2020 18:53:57 +1000
Nick Coghlan  wrote:

> On Sat, 31 Oct 2020 at 18:20, Paul Sokolovsky 
> wrote:
> > I blame it on my Saturday's hazy mind, but also for the interest of
> > other readers, could you elaborate on "`==` interacts poorly with
> > other aspects of the pattern syntax"?  
> 
[]
> case SomeClass(some_attr=):
[]
> 
> If match patterns can start with "=", those both end up looking pretty
> weird (and that's assuming the tokenizer can even figure out what is
> happening in the first place - if you omitted the spaces before "=="
> in the examples below, it would come up with "== =" for "===" and ":=
> =" for ":=="):

Of course, there would need to be a space between "=" and "==", so
those could be recognized as exactly those tokens, and not as another
token, "===".

If you hint that it would be the first case of that in Python, then ...
yeah, seems to be so. But then, pattern matching is the first case when
a seemingly r-value looking expression, like 'Cls(a, b)' can actually
be l-value (i.e. bind/assign to variables). I'm afraid, we won't go
thru this journey without expanding perceptual horizons...

> 
> case SomeClass(some_attr= ==EXPR):

I agree this looks ugly.

> ...
> case {some.key: ==EXPR}:

IMHO, this looks less unpretty than above, almost Ok.

> Most ad hoc examples seem to lean towards sequence matching (I guess
> because they're shorter), but it's accounting for the readability of
> class and mapping patterns that places the most constraints on the
> choice of prefix sigil.

Right, because many people are familiar with pattern matching from
languages where it's applied to algebraic data types, a clean,
simple (bordering on primitive) concept. Whereas problem of Python is
that it tries to "embrace unembraceable". And as it tries to do so,
complex things will necessarily look cumbersome, and would require
special treatment.

We already use "def foo(kw1: ann = 1, k2: ann2 = 2)" syntax, and so can
use:

> case SomeClass(some_attr = ==EXPR):

Which no longer looks as ugly as before (just unpretty).


But anyway, the talk is about using an explicit sigil, which one is
hopefully still being considered. So, "==" as a sigil has issues, but
then "?" too. There was a (seemingly) novel idea of using unary plus,
"+" as a sigil, with a clear meaning which can be derived from its
usual Python semantics: "get value of an expression (variable as a
particular case)". What about it?

"By abuse of notation" it could be used as a wildcard match too ;-).
(But as-value sigil and wildcard syntax are 2 orthogonal questions,
while PEP 642 goes a long way to conflate them, and presents that
conflation as a selling point.)


> Cheers,
> Nick.

-- 
Best regards,
 Paul  mailto:pmis...@gmail.com
___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/IUFWUVFM4JTYZC2W5UQ6C5HZNI3QLGWY/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Pattern matching reborn: PEP 622 is dead, long live PEP 634, 635, 636

2020-10-31 Thread Nick Coghlan
On Sat, 31 Oct 2020 at 18:20, Paul Sokolovsky  wrote:
> I blame it on my Saturday's hazy mind, but also for the interest of
> other readers, could you elaborate on "`==` interacts poorly with other
> aspects of the pattern syntax"?

Class patterns use the keyword argument syntax to match by attribute name:

case SomeClass(some_attr=):
...

Mapping patterns use dictionary syntax:

case {some.key:}:
...

If match patterns can start with "=", those both end up looking pretty
weird (and that's assuming the tokenizer can even figure out what is
happening in the first place - if you omitted the spaces before "=="
in the examples below, it would come up with "== =" for "===" and ":=
=" for ":=="):

case SomeClass(some_attr= ==EXPR):
...
case {some.key: ==EXPR}:
...

Most ad hoc examples seem to lean towards sequence matching (I guess
because they're shorter), but it's accounting for the readability of
class and mapping patterns that places the most constraints on the
choice of prefix sigil.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/UY4MXJG46RFBBM25XOLHOVOZQ7HJKZI3/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Pattern matching reborn: PEP 622 is dead, long live PEP 634, 635, 636

2020-10-31 Thread Paul Sokolovsky
Hello,

On Sat, 31 Oct 2020 18:01:16 +1000
Nick Coghlan  wrote:

> On Sat, 31 Oct 2020 at 17:49, Paul Sokolovsky 
> wrote:
> > Alternatively, can add "inline guard sigils" for the most common
> > guard conditions, which would be equality, period:
> >
> > match r:
> > case (src, None):
> > case (==c, msg):
> > case (==s, msg):  
> 
> `==` interacts poorly with other aspects of the pattern syntax (most
> notably keywords in class patterns), but PEP 642's identity constraint
> expressions would allow:

Nice to hear that there're (high-hierarchy) people who want to do 2nd
round on intent-explicitizing sigils, thanks.

I blame it on my Saturday's hazy mind, but also for the interest of
other readers, could you elaborate on "`==` interacts poorly with other
aspects of the pattern syntax"?

To elaborate from my side, I'm talking about introducing unary prefix
operator "==". That should be pretty disambiguated on its own. (Of
course, such an operator would be allowed only in the expression syntax
for the "case" clause. The biggest risk my hazy mind sees is founding
fathers' fixation with doing any grammar disambiguation on the level of
the (flat) grammar, which leads to atrocious grammars. Whereas a
practical person would just go for a level-above check).

> 
>  match r:
> case (src, ?is None):
> ... # Any src, msg is None
> case (?is c, msg):
> ... # Any msg, src is c
> case (?is s, msg):
> ... # Any msg, src is s

So, you're talking about introducing unary prefix operators "?" and
"?is". And while "?is" is clear (though not pretty), I don't see how the
syntax of "?" translates to the meaning you assign to it, "lookup
value of".

(Btw, just thought, unary "+" could be used for "value lookup" syntax.
At least, with such usage, it will be "mathematically understandable".
Actually, would anything need to be done at all to use unary "+" in
this meaning? Hmm, someone must have thought about that before...)


> Cheers,
> Nick.


-- 
Best regards,
 Paul  mailto:pmis...@gmail.com
___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/QPYBAPOSMXK7XDETO5XB5GNFITI6JPTN/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Pattern matching reborn: PEP 622 is dead, long live PEP 634, 635, 636

2020-10-31 Thread Nick Coghlan
On Sat, 31 Oct 2020 at 17:49, Paul Sokolovsky  wrote:
> Alternatively, can add "inline guard sigils" for the most common guard
> conditions, which would be equality, period:
>
> match r:
> case (src, None):
> case (==c, msg):
> case (==s, msg):

`==` interacts poorly with other aspects of the pattern syntax (most
notably keywords in class patterns), but PEP 642's identity constraint
expressions would allow:

 match r:
case (src, ?is None):
... # Any src, msg is None
case (?is c, msg):
... # Any msg, src is c
case (?is s, msg):
... # Any msg, src is s

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/MVJY42LZM7ITRMZ3TSHC23GOMKIS6FCJ/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Pattern matching reborn: PEP 622 is dead, long live PEP 634, 635, 636

2020-10-31 Thread Paul Sokolovsky
Hello,

On Sat, 31 Oct 2020 02:37:14 +0100
Tin Tvrtković  wrote:

[]


> async def ws_proxy(client: WebSocket):
> await client.accept()
> async with ClientSession() as session:
> async with session.ws_connect("wss://echo.websocket.org") as
> s: c = starlette_websocket_iterator(client)
> async for r in wait_on(c, s):
> match r:
> case (src, None):
> print(f"{src} closed the connection")
> break
> case (src, msg) if src is c:
> print(f"CLIENT: {msg}")
> await s.send_str(msg)
> case (src, msg) if src is s:
> print(f"SERVER: {msg}")
> await client.send_text(msg.data)
> 
> So yeah, the helper yields tuples of the source and message, using
> None as a sentinel for closure. Guards are used to match on the
> source, using iterator identity.


> My first version just used `case (s, msg):` hoping to match on the
> identity of s, but that doesn't work since s is not a literal.
> 
> ..., I'd say
> this style of programming is quite readable and understandable.

It baffled me how the first sentence contradicts the second.


So, with Mark Shannon's recent (re)criticism of pattern matching PEP,
in particular "Can pattern matching make it clear what is assigned?" 
anyone wants to go 2nd round on "sigils aren't that bad,
especially comparing to what we're offered otherwise"?


E.g. would it really be that bad to have: 

match r:
case (@src, None):
case (c, @msg):
case (s, @msg):

Alternative syntax, arguably, even newbie can pick it up what it does:

match r:
case (>src, None):
case (c, >msg):
case (s, >msg):
 
Alternatively, can add "inline guard sigils" for the most common guard
conditions, which would be equality, period:

match r:
case (src, None):
case (==c, msg):
case (==s, msg):


[]

-- 
Best regards,
 Paul  mailto:pmis...@gmail.com
___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/FDSJOO6KLGSW7SAGBR4W2KHKHBVKVETB/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Pattern matching reborn: PEP 622 is dead, long live PEP 634, 635, 636

2020-10-30 Thread Tin Tvrtković
A small update on this, since I've been playing with it.

I'm trying to implement a websocket proxy, since it's an example of a toy
project that needs to juggle two long-lived asyncio connections at once.
I'm using Starlette/Uvicorn for the server part (the part that accepts the
connection) and aiohttp's client functionality to connect to an echo server
on the Internet.

I've settled on the async iterator interface as a good building block for
this, not queues. Turns out an async iterator looks very much as the read
interface for a Go channel (they spit out values and signal their closure).
Aiohttp already provides an async iterator interface for reading from the
upstream server. Starlette doesn't, but an adapter is very easy to write.

Now that we've settled on a suitable interface, a helper function (wait_on)
is easy to write. (Code omitted for brevity.) This is the entire request
handler:

async def ws_proxy(client: WebSocket):
await client.accept()
async with ClientSession() as session:
async with session.ws_connect("wss://echo.websocket.org") as s:
c = starlette_websocket_iterator(client)
async for r in wait_on(c, s):
match r:
case (src, None):
print(f"{src} closed the connection")
break
case (src, msg) if src is c:
print(f"CLIENT: {msg}")
await s.send_str(msg)
case (src, msg) if src is s:
print(f"SERVER: {msg}")
await client.send_text(msg.data)

So yeah, the helper yields tuples of the source and message, using None as
a sentinel for closure. Guards are used to match on the source, using
iterator identity. My first version just used `case (s, msg):` hoping to
match on the identity of s, but that doesn't work since s is not a literal.

I'd say this is pretty cool. With the ecosystem moving to async iterators
for streams of data (or just writing adapters), I'd say this style of
programming is quite readable and understandable.

On Tue, Oct 27, 2020 at 1:16 AM Tin Tvrtković  wrote:

> Hello,
>
> Go channels are indeed very similar to asyncio Queues, with some added
> features like channels being closable. (There is also special syntax in the
> select statement, `val, ok <- chan`, that will set the `ok` variable to
> false if the channel has been closed.) A larger difference, I think, is
> that in Go channels are used practically everywhere, more so than asyncio
> Queues. They are an abstraction the vast majority of Go concurrency is
> built upon.
>
> Building this for asyncio tasks, instead of just queues, would be much
> more useful in Python.
>
> Contemplating this some more, I would agree we don't need an async match.
> A function and some types to match on would probably be enough to get us
> close to a select statement in a PEP634 Python. I guess the challenge is
> designing these matchable types for ease of use now, and I need to study
> the pattern matching PEPs in more detail to be able to contribute here.
>
> On one hand, this means this problem can be solved by a third party
> library. On the other hand, I feel like this would be very useful so it
> might be worth it to have it somewhere in the stdlib asyncio namespace.
>
> Since `asyncio.wait` can yield multiple tasks in the completed set, this
> would probably have to be wrapped in an `async for`.
>
>
>
> On Mon, Oct 26, 2020 at 12:33 PM Gustavo Carneiro 
> wrote:
>
>> It's true that asyncio.wait provides the tools that you need, but it's a
>> bit clunky to use correctly.
>>
>> Maybe would be something along the lines of:
>>
>> --
>> queue1 = asyncio.Queue()
>> queue2 = asyncio.Queue()
>> ...
>> get1 = asyncio.create_task(queue1.get())
>> get2 = asyncio.create_task(queue2.get())
>> await asyncio.wait({get1, get2}, return_when=asyncio.FIRST_COMPLETED)
>> match [task.done() for task in (get1, get2)]:
>> case [True, False]:  get2.cancel(); item1 = await get1; 
>> case [False, True]:  get1.cancel(); item2 = await get2; 
>> case [True, True]:  item1 = await get1; ; item2 = await get2;
>> 
>> --
>>
>> If asyncio.Queue() is the equivalent of Go channels, perhaps it would be
>> worth designing a new API for asyncio.Queue, one that is better suited to
>> the match statement:
>>
>> class Queue:
>>async def read_wait(self) -> 'Queue':
>>"""
>>Waits until the queue has at least one item ready to read, without
>> actually consuming the item.
>>"""
>>
>> Then we could more easily use match statement with multiple queues, thus:
>>
>> --
>> async def ready_queue(*queues: asyncio.Queue) -> asyncio.Queue:
>>"""
>>Take multiple queue parameters and waits for at least one of them to
>> have items pending to read, returning that queue.
>>"""
>>await asyncio.wait({queue.read_wait() for queue in queues},
>> 

[Python-Dev] Re: Pattern matching reborn: PEP 622 is dead, long live PEP 634, 635, 636

2020-10-26 Thread Tin Tvrtković
Hello,

Go channels are indeed very similar to asyncio Queues, with some added
features like channels being closable. (There is also special syntax in the
select statement, `val, ok <- chan`, that will set the `ok` variable to
false if the channel has been closed.) A larger difference, I think, is
that in Go channels are used practically everywhere, more so than asyncio
Queues. They are an abstraction the vast majority of Go concurrency is
built upon.

Building this for asyncio tasks, instead of just queues, would be much more
useful in Python.

Contemplating this some more, I would agree we don't need an async match. A
function and some types to match on would probably be enough to get us
close to a select statement in a PEP634 Python. I guess the challenge is
designing these matchable types for ease of use now, and I need to study
the pattern matching PEPs in more detail to be able to contribute here.

On one hand, this means this problem can be solved by a third party
library. On the other hand, I feel like this would be very useful so it
might be worth it to have it somewhere in the stdlib asyncio namespace.

Since `asyncio.wait` can yield multiple tasks in the completed set, this
would probably have to be wrapped in an `async for`.



On Mon, Oct 26, 2020 at 12:33 PM Gustavo Carneiro 
wrote:

> It's true that asyncio.wait provides the tools that you need, but it's a
> bit clunky to use correctly.
>
> Maybe would be something along the lines of:
>
> --
> queue1 = asyncio.Queue()
> queue2 = asyncio.Queue()
> ...
> get1 = asyncio.create_task(queue1.get())
> get2 = asyncio.create_task(queue2.get())
> await asyncio.wait({get1, get2}, return_when=asyncio.FIRST_COMPLETED)
> match [task.done() for task in (get1, get2)]:
> case [True, False]:  get2.cancel(); item1 = await get1; 
> case [False, True]:  get1.cancel(); item2 = await get2; 
> case [True, True]:  item1 = await get1; ; item2 = await get2; 
> --
>
> If asyncio.Queue() is the equivalent of Go channels, perhaps it would be
> worth designing a new API for asyncio.Queue, one that is better suited to
> the match statement:
>
> class Queue:
>async def read_wait(self) -> 'Queue':
>"""
>Waits until the queue has at least one item ready to read, without
> actually consuming the item.
>"""
>
> Then we could more easily use match statement with multiple queues, thus:
>
> --
> async def ready_queue(*queues: asyncio.Queue) -> asyncio.Queue:
>"""
>Take multiple queue parameters and waits for at least one of them to
> have items pending to read, returning that queue.
>"""
>await asyncio.wait({queue.read_wait() for queue in queues},
> return_when=asyncio.FIRST_COMPLETED)
>for queue in queues:
>   if queue.qsize() > 0:
>   return queue
>
> ...
>
> queue1 = asyncio.Queue()
> queue2 = asyncio.Queue()
>
> ...
>
> match await ready_queue(queue1, queue2):
> case queue1:  item1 = queue1.get_nowait(); 
> case queue2:  item2 = queue2.get_nowait(); 
> --
>
> Which is less clunky, maybe?...
>
> The above is not 100% bug free.  I think those queue.get_nowait() calls
> may still end up raising QueueEmpty exceptions, in case there is another
> concurrent reader for those queues.  This code would need more work, most
> likely.
>
> In any case, perhaps it's not the match statement that needs to change,
> but rather asyncio API that needs to be enhanced.
>
>
> On Sun, 25 Oct 2020 at 01:14, Nick Coghlan  wrote:
>
>> On Sat., 24 Oct. 2020, 4:21 am Guido van Rossum, 
>> wrote:
>>
>>> On Fri, Oct 23, 2020 at 6:19 AM Tin Tvrtković 
>>> wrote:
>>>
 Hi,

 first of all, I'm a big fan of the changes being proposed here since in
 my code I prefer the 'union' style of logic over the OO style.

 I was curious, though, if there are any plans for the match operator to
 support async stuff. I'm interested in the problem of waiting on multiple
 asyncio tasks concurrently, and having a branch of code execute depending
 on the task.

 Currently this can be done by using asyncio.wait, looping over the
 done set and executing an if-else chain there, but this is quite tiresome.
 Go has a select statement (https://tour.golang.org/concurrency/5) that
 looks like this:

 select {
 case <-ch1:
 fmt.Println("Received from ch1")
 case <-ch2:
 fmt.Println("Received from ch2")
 }

 Speaking personally, this is a Go feature I miss a lot when writing
 asyncio code. The syntax is similar to what's being proposed here. Although
 it could be a separate thing added later, async match, I guess.

>>>
>>> Hadn't seen this before. You could propose this as a follow-up for 3.11.
>>> But aren't Go channels more like asyncio Queues? I guess we'd need way more
>>> in terms of a worked-out example (using asyncio code, not Go code).
>>>
>>
>> I think we'd also want to see how far folks get with 

[Python-Dev] Re: Pattern matching reborn: PEP 622 is dead, long live PEP 634, 635, 636

2020-10-26 Thread Gustavo Carneiro
It's true that asyncio.wait provides the tools that you need, but it's a
bit clunky to use correctly.

Maybe would be something along the lines of:

--
queue1 = asyncio.Queue()
queue2 = asyncio.Queue()
...
get1 = asyncio.create_task(queue1.get())
get2 = asyncio.create_task(queue2.get())
await asyncio.wait({get1, get2}, return_when=asyncio.FIRST_COMPLETED)
match [task.done() for task in (get1, get2)]:
case [True, False]:  get2.cancel(); item1 = await get1; 
case [False, True]:  get1.cancel(); item2 = await get2; 
case [True, True]:  item1 = await get1; ; item2 = await get2; 
--

If asyncio.Queue() is the equivalent of Go channels, perhaps it would be
worth designing a new API for asyncio.Queue, one that is better suited to
the match statement:

class Queue:
   async def read_wait(self) -> 'Queue':
   """
   Waits until the queue has at least one item ready to read, without
actually consuming the item.
   """

Then we could more easily use match statement with multiple queues, thus:

--
async def ready_queue(*queues: asyncio.Queue) -> asyncio.Queue:
   """
   Take multiple queue parameters and waits for at least one of them to
have items pending to read, returning that queue.
   """
   await asyncio.wait({queue.read_wait() for queue in queues},
return_when=asyncio.FIRST_COMPLETED)
   for queue in queues:
  if queue.qsize() > 0:
  return queue

...

queue1 = asyncio.Queue()
queue2 = asyncio.Queue()

...

match await ready_queue(queue1, queue2):
case queue1:  item1 = queue1.get_nowait(); 
case queue2:  item2 = queue2.get_nowait(); 
--

Which is less clunky, maybe?...

The above is not 100% bug free.  I think those queue.get_nowait() calls may
still end up raising QueueEmpty exceptions, in case there is another
concurrent reader for those queues.  This code would need more work, most
likely.

In any case, perhaps it's not the match statement that needs to change, but
rather asyncio API that needs to be enhanced.


On Sun, 25 Oct 2020 at 01:14, Nick Coghlan  wrote:

> On Sat., 24 Oct. 2020, 4:21 am Guido van Rossum,  wrote:
>
>> On Fri, Oct 23, 2020 at 6:19 AM Tin Tvrtković 
>> wrote:
>>
>>> Hi,
>>>
>>> first of all, I'm a big fan of the changes being proposed here since in
>>> my code I prefer the 'union' style of logic over the OO style.
>>>
>>> I was curious, though, if there are any plans for the match operator to
>>> support async stuff. I'm interested in the problem of waiting on multiple
>>> asyncio tasks concurrently, and having a branch of code execute depending
>>> on the task.
>>>
>>> Currently this can be done by using asyncio.wait, looping over the done
>>> set and executing an if-else chain there, but this is quite tiresome. Go
>>> has a select statement (https://tour.golang.org/concurrency/5) that
>>> looks like this:
>>>
>>> select {
>>> case <-ch1:
>>> fmt.Println("Received from ch1")
>>> case <-ch2:
>>> fmt.Println("Received from ch2")
>>> }
>>>
>>> Speaking personally, this is a Go feature I miss a lot when writing
>>> asyncio code. The syntax is similar to what's being proposed here. Although
>>> it could be a separate thing added later, async match, I guess.
>>>
>>
>> Hadn't seen this before. You could propose this as a follow-up for 3.11.
>> But aren't Go channels more like asyncio Queues? I guess we'd need way more
>> in terms of a worked-out example (using asyncio code, not Go code).
>>
>
> I think we'd also want to see how far folks get with using guard clauses
> for this kind of "where did the data come from?" check - the only
> specifically asynchronous bit would be the "await multiple tasks"
> operation, and you can already tell asyncio.wait() to return on the first
> completed task rather than waiting for all the results.
>
> Cheers,
> Nick.
>
>
>
>> ___
> Python-Dev mailing list -- python-dev@python.org
> To unsubscribe send an email to python-dev-le...@python.org
> https://mail.python.org/mailman3/lists/python-dev.python.org/
> Message archived at
> https://mail.python.org/archives/list/python-dev@python.org/message/NQWYLFLGLLCEHAXYHUOXQ3M7IOEL65ET/
> Code of Conduct: http://python.org/psf/codeofconduct/
>


-- 
Gustavo J. A. M. Carneiro
Gambit Research
"The universe is always one step beyond logic." -- Frank Herbert
___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/BHHNI54W6PVET3RD7XVHNOHFUAEDSVS5/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Pattern matching reborn: PEP 622 is dead, long live PEP 634, 635, 636

2020-10-24 Thread Nick Coghlan
On Sat., 24 Oct. 2020, 4:21 am Guido van Rossum,  wrote:

> On Fri, Oct 23, 2020 at 6:19 AM Tin Tvrtković 
> wrote:
>
>> Hi,
>>
>> first of all, I'm a big fan of the changes being proposed here since in
>> my code I prefer the 'union' style of logic over the OO style.
>>
>> I was curious, though, if there are any plans for the match operator to
>> support async stuff. I'm interested in the problem of waiting on multiple
>> asyncio tasks concurrently, and having a branch of code execute depending
>> on the task.
>>
>> Currently this can be done by using asyncio.wait, looping over the done
>> set and executing an if-else chain there, but this is quite tiresome. Go
>> has a select statement (https://tour.golang.org/concurrency/5) that
>> looks like this:
>>
>> select {
>> case <-ch1:
>> fmt.Println("Received from ch1")
>> case <-ch2:
>> fmt.Println("Received from ch2")
>> }
>>
>> Speaking personally, this is a Go feature I miss a lot when writing
>> asyncio code. The syntax is similar to what's being proposed here. Although
>> it could be a separate thing added later, async match, I guess.
>>
>
> Hadn't seen this before. You could propose this as a follow-up for 3.11.
> But aren't Go channels more like asyncio Queues? I guess we'd need way more
> in terms of a worked-out example (using asyncio code, not Go code).
>

I think we'd also want to see how far folks get with using guard clauses
for this kind of "where did the data come from?" check - the only
specifically asynchronous bit would be the "await multiple tasks"
operation, and you can already tell asyncio.wait() to return on the first
completed task rather than waiting for all the results.

Cheers,
Nick.



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


[Python-Dev] Re: Pattern matching reborn: PEP 622 is dead, long live PEP 634, 635, 636

2020-10-23 Thread Neil Schemenauer
Woah, this is both exciting and scary.  If adopted, it will be a major 
change to how Python programs are written.  It seems a lot of work has 
been put into polishing the design.  That's good because if we do this, 
will not be easy to fix things later if we made design errors.


One of my first thoughts is this sounds similar to Clojure's "spec"[1].  
Are the pattern matching PEP authors aware of it?  I don't mean we need 
to copy what spec does (really, I'm not all that familiar with it).  I 
do notice that spec patterns are not just used for case statement 
de-structuring.  Maybe we should think about a future Python that would 
use similar patterns for those things too.  I believe "spec" has been in 
use for a number of years and so the Clojure community has useful 
experience with the design.


An example of something "spec" does and this proposal agrees on is the 
matching of mappings.  I.e. that if there are additional key/value 
pairs, they don't cause the match to fail by default. That's important 
for loose coupling between systems.



[1] https://clojure.org/about/spec

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


[Python-Dev] Re: Pattern matching reborn: PEP 622 is dead, long live PEP 634, 635, 636

2020-10-23 Thread Guido van Rossum
On Fri, Oct 23, 2020 at 6:19 AM Tin Tvrtković  wrote:

> Hi,
>
> first of all, I'm a big fan of the changes being proposed here since in my
> code I prefer the 'union' style of logic over the OO style.
>
> I was curious, though, if there are any plans for the match operator to
> support async stuff. I'm interested in the problem of waiting on multiple
> asyncio tasks concurrently, and having a branch of code execute depending
> on the task.
>
> Currently this can be done by using asyncio.wait, looping over the done
> set and executing an if-else chain there, but this is quite tiresome. Go
> has a select statement (https://tour.golang.org/concurrency/5) that looks
> like this:
>
> select {
> case <-ch1:
> fmt.Println("Received from ch1")
> case <-ch2:
> fmt.Println("Received from ch2")
> }
>
> Speaking personally, this is a Go feature I miss a lot when writing
> asyncio code. The syntax is similar to what's being proposed here. Although
> it could be a separate thing added later, async match, I guess.
>

Hadn't seen this before. You could propose this as a follow-up for 3.11.
But aren't Go channels more like asyncio Queues? I guess we'd need way more
in terms of a worked-out example (using asyncio code, not Go code).
-- 
--Guido van Rossum (python.org/~guido)
*Pronouns: he/him **(why is my pronoun here?)*

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


[Python-Dev] Re: Pattern matching reborn: PEP 622 is dead, long live PEP 634, 635, 636

2020-10-23 Thread Tin Tvrtković
Hi,

first of all, I'm a big fan of the changes being proposed here since in my
code I prefer the 'union' style of logic over the OO style.

I was curious, though, if there are any plans for the match operator to
support async stuff. I'm interested in the problem of waiting on multiple
asyncio tasks concurrently, and having a branch of code execute depending
on the task.

Currently this can be done by using asyncio.wait, looping over the done set
and executing an if-else chain there, but this is quite tiresome. Go has a
select statement (https://tour.golang.org/concurrency/5) that looks like
this:

select {
case <-ch1:
fmt.Println("Received from ch1")
case <-ch2:
fmt.Println("Received from ch2")
}

Speaking personally, this is a Go feature I miss a lot when writing asyncio
code. The syntax is similar to what's being proposed here. Although it
could be a separate thing added later, async match, I guess.


> Message: 2
> Date: Thu, 22 Oct 2020 09:48:54 -0700
> From: Guido van Rossum 
> Subject: [Python-Dev] Pattern matching reborn: PEP 622 is dead, long
> live PEP 634, 635, 636
> To: Python-Dev 
> Message-ID:
>  jock2if496st00ke8mg921yp5z...@mail.gmail.com>
> Content-Type: multipart/alternative;
> boundary="39e83905b2453edf"
>
> --39e83905b2453edf
> Content-Type: text/plain; charset="UTF-8"
>
> After the pattern matching discussion died out, we discussed it with the
> Steering Council. Our discussion ended fairly positive, but there were a
> lot of problems with the text. We decided to abandon the old PEP 622 and
> break it up into three parts:
>
> - PEP 634: Specification
> - PEP 635: Motivation and Rationale
> - PEP 636: Tutorial
>
> This turned out to be more work than I had expected (basically we wrote all
> new material) but we've finally made it to a point where we can request
> feedback and submit the new version to the SC for approval.
>
> While the text of the proposal is completely different, there aren't that
> many substantial changes:
>
> - We changed walrus patterns ('v := p') to AS patterns ('p as v').
> - We changed the method of comparison for literals None, False, True to use
> 'is' instead of '=='.
> - SyntaxError if an irrefutable case[1] is followed by another case block.
> - SyntaxError if an irrefutable pattern[1] occurs on the left of '|', e.g.
> 'x | [x]'.
> - We dropped the `@sealed` decorator and everything aimed at static type
> checkers.
>
> [1] An irrefutable pattern is one that never fails, notably a wildcard or a
> capture. An irrefutable case has an irrefutable pattern at the top and no
> guard. Irrefutability is defined recursively, since an '|' with an
> irrefutable pattern on either side is itself irrefutable, and so is an AS
> pattern with an irrefutable pattern before 'as'.
>
> The following issues were specifically discussed with the SC:
>
> - Concerns about side effects and undefined behavior. There's now some
> specific language about this in a few places (giving the compiler freedom
> to optimize), and a section "Side Effects and Undefined Behavior".
>
> - Footgun if `case NAME:` is followed by another case. This is now a
> SyntaxError.
>
> - Adding an 'else' clause. We decided not to add this; motivation in PEP
> 635.
>
> - Alternative 'OR' symbol. Not changed; see PEP 635.
>
> - Alternative wildcard symbol. Not changed, but Thomas wrote PEP 640 which
> proposes '?' as a general assignment target. PEP 635 has some language
> against that idea.
>
> - Alternative indentation schemes. We decided to stick with the original
> proposal; see PEP 635.
>
> - Marking all capture variables with a sigil. We all agreed this was a bad
> idea; see PEP 635.
>
> --
> --Guido van Rossum (python.org/~guido)
> *Pronouns: he/him **(why is my pronoun here?)*
>
___
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/GQP2EXF4EC5FCUJPTRY2CTLNHLWHD5GY/
Code of Conduct: http://python.org/psf/codeofconduct/


[Python-Dev] Re: Pattern matching reborn: PEP 622 is dead, long live PEP 634, 635, 636

2020-10-22 Thread Steven D'Aprano
On Thu, Oct 22, 2020 at 09:48:54AM -0700, Guido van Rossum wrote:
> After the pattern matching discussion died out, we discussed it with the
> Steering Council. Our discussion ended fairly positive, but there were a
> lot of problems with the text. We decided to abandon the old PEP 622 and
> break it up into three parts:
> 
> - PEP 634: Specification
> - PEP 635: Motivation and Rationale
> - PEP 636: Tutorial

Nice!

(By coincidence, I had stumbled across them a few days ago, and hadn't 
realised that they were new. I hadn't looked at the dates and just 
assumed they were the original proposal.)

I haven't read through them in detail but on broad skimming this looks 
very nice indeed.


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


[Python-Dev] Re: Pattern matching reborn: PEP 622 is dead, long live PEP 634, 635, 636

2020-10-22 Thread Guido van Rossum
On Thu, Oct 22, 2020 at 10:42 AM MRAB  wrote:

> [Guido]
> > - SyntaxError if an irrefutable case[1] is followed by another case
> block.
> > - SyntaxError if an irrefutable pattern[1] occurs on the left of '|',
> > e.g. 'x | [x]'.
>
> Is "syntax error" that the right term for it? Maybe it should be
> something different, a subclass of SyntaxError perhaps.
>

I think it is a SyntaxError. It is detected without needing to look at the
symbol table, although it is not encoded in the grammar (we probably could,
but it would be very tedious), so it's done during a later stage. We have
many of these (e.g. 'return' outside function, also not encoded in the
grammar).

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

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


[Python-Dev] Re: Pattern matching reborn: PEP 622 is dead, long live PEP 634, 635, 636

2020-10-22 Thread MRAB

On 2020-10-22 17:48, Guido van Rossum wrote:
After the pattern matching discussion died out, we discussed it with the 
Steering Council. Our discussion ended fairly positive, but there were a 
lot of problems with the text. We decided to abandon the old PEP 622 and 
break it up into three parts:


- PEP 634: Specification
- PEP 635: Motivation and Rationale
- PEP 636: Tutorial

This turned out to be more work than I had expected (basically we wrote 
all new material) but we've finally made it to a point where we can 
request feedback and submit the new version to the SC for approval.


While the text of the proposal is completely different, there aren't 
that many substantial changes:


- We changed walrus patterns ('v := p') to AS patterns ('p as v').
- We changed the method of comparison for literals None, False, True to 
use 'is' instead of '=='.

- SyntaxError if an irrefutable case[1] is followed by another case block.
- SyntaxError if an irrefutable pattern[1] occurs on the left of '|', 
e.g. 'x | [x]'.


Is "syntax error" that the right term for it? Maybe it should be 
something different, a subclass of SyntaxError perhaps.


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