[Python-Dev] async/await in Python; v2 (original) (raw)
PJ Eby pje at telecommunity.com
Wed Apr 22 21:44:46 CEST 2015
- Previous message (by thread): [Python-Dev] async/await in Python; v2
- Next message (by thread): [Python-Dev] async/await in Python; v2
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
On Tue, Apr 21, 2015 at 1:26 PM, Yury Selivanov <yselivanov.ml at gmail.com> wrote:
It is an error to pass a regular context manager without
_aenter_
and_aexit_
methods toasync with
. It is aSyntaxError
to useasync with
outside of a coroutine.
I find this a little weird. Why not just have with
and for
inside
a coroutine dynamically check the iterator or context manager, and
either behave sync or async accordingly? Why must there be a
syntactic difference?
Not only would this simplify the syntax, it would also allow dropping
the need for async
to be a true keyword, since functions could be
defined via "def async foo():" rather than "async def foo():"
...which, incidentally, highlights one of the things that's been bothering me about all this "async foo" stuff: "async def" looks like it defines the function asynchronously (as with "async with" and "async for"), rather than defining an asynchronous function. ISTM it should be "def async bar():" or even "def bar() async:".
Also, even that seems suspect to me: if await
looks for an await
method and simply returns the same object (synchronously) if the
object doesn't have an await method, then your code sample that
supposedly will fail if a function ceases to be a coroutine will not
actually fail.
In my experience working with coroutine systems, making a system polymorphic (do something appropriate with what's given) and idempotent (don't do anything if what's wanted is already done) makes it more robust. In particular, it eliminates the issue of mixing coroutines and non-coroutines.
To sum up: I can see the use case for a new await
distinguished from
yield
, but I don't see the need to create new syntax for everything;
ISTM that adding the new asynchronous protocols and using them on
demand is sufficient. Marking a function asynchronous so it can use
asynchronous iteration and context management seems reasonably useful,
but I don't think it's terribly important for the type of function
result. Indeed, ISTM that the built-in object
class could just
implement __await__
as a no-op returning self, and then all
results are trivially asynchronous results and can be awaited
idempotently, so that awaiting something that has already been waited
for is a no-op. (Prior art: the Javascript Promise.resolve() method,
which takes either a promise or a plain value and returns a promise,
so that you can write code which is always-async in the presence of
values that may already be known.)
Finally, if the async for and with operations have to be distinguished
by syntax at the point of use (vs. just always being used in
coroutines), then ISTM that they should be with async foo:
and for async x in bar:
, since the asynchronousness is just an aspect of how
the main keyword is executed.
tl;dr: I like the overall ideas but hate the syntax and type segregation involved: declaring a function async at the top is OK to enable async with/for semantics and await expressions, but the rest seems unnecessary and bad for writing robust code. (e.g. note that requiring different syntax means a function must either duplicate code or restrict its input types more, and type changes in remote parts of the program will propagate syntax changes throughout.)
- Previous message (by thread): [Python-Dev] async/await in Python; v2
- Next message (by thread): [Python-Dev] async/await in Python; v2
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]