> On Apr 24, 2015, at 10:03 AM, Guido van Rossum <gu...@python.org> wrote:
> 
> 1. precise syntax of `async def`
> 
> So I still prefer `async def`.

Me too. Also, we would use a similar vocabulary to existing users of the 
feature. This is exactly how Hack does it: 
http://docs.hhvm.com/manual/en/hack.async.php 
<http://docs.hhvm.com/manual/en/hack.async.php>, how ECMAScript 7 proposes it: 
http://wiki.ecmascript.org/doku.php?id=strawman:async_functions 
<http://wiki.ecmascript.org/doku.php?id=strawman:async_functions> and similarly 
to how C# does it (async comes after the public/private modifier but before the 
return type): https://msdn.microsoft.com/en-us/library/hh156513.aspx 
<https://msdn.microsoft.com/en-us/library/hh156513.aspx>


> 2. do we need `async for` and `async with`
> 
> Yes we do.

+1


> (Though maybe we should consider `await for` and `await with`? That would 
> have the advantage of making it easy to scan for all suspension points by 
> searching for /await/. But being a verb it doesn't read very well.)

I’m on the fence here.

OT1H, I think “await for something in a_container” and “await with 
a_context_manager():” also read pretty well. It’s also more consistent with 
being the one way of finding “yield points”.

OTOH, “await with a_context_manager():” suggests the entire statement is 
awaited on, which is not true. “async with” is more opaque in this way, it 
simply states “there are going to be implementation-specific awaits inside”. So 
it’s more consistent with “async def foo()”.

All in all I think I’m leaning towards “async for” and “async with”. More 
importantly though, I’m wondering how obvious will the failure mode be when 
somebody uses a bare “for” instead of an “async for”. Ditto for “with” vs. 
“async with”. How much debugging will be necessary to find that it’s only a 
missing “async” before the loop?


Side note: to add to the confusion about syntax, Hack’s equivalent for “async 
for” - which doesn’t really translate well to Python - uses “await” and ties it 
to the iterable:

foreach ($list await as $input) { … }

The equivalent in Python would be:

for input in list await:

Notably, this is ugly and would be confused with `for input in await list:` 
which means something different.

Also, this particular construct represents less than 0.01% of all “await” 
occurences in Facebook code, suggesting it’s not performance critical.


> 3. syntactic priority of `await`
> 
> Yury, could you tweak the syntax for `await` so that we can write the most 
> common usages without parentheses?

+1

Yury points out there was likely a reason this wasn’t the case for `yield` in 
the first place. It would be good to revisit that. Maybe for yield itself, too?


> 4. `cocall` vs. `await`
> 
> I just can't get used to this aspect of PEP 3152, so I'm rejecting it.

+1


> (Yury: PEP 492 is not accepted yet, but you're getting closer.)

May I suggest using the bat-signal to summon Glyph to confirm this is going to 
be helpful/usable with Twisted as well?


> 5. do we really need `__aiter__` and friends
> 
> There's a lot of added complexity, but I think it's worth it. I don't think 
> we need to make the names longer, the 'a' prefix is fine for these methods.

+1


> 6. StopAsyncException
> 
> I'm not sure about this. The motivation given in the PEP seems to focus on 
> the need for `__anext__` to be async. But is this really the right pattern? 
> What if we required `ait.__anext__()` to return a future, which can either 
> raise good old `StopIteration` or return the next value from the iteration 
> when awaited? I'm wondering if there are a few alternatives to be explored 
> around the async iterator protocol still.

So are you suggesting to pass the returned value in a future? In this case the 
future would need to be passed to __anext__, so the Cursor example from the PEP 
would look like this:

class Cursor:
    def __init__(self):
        self.buffer = collections.deque()

    def _prefetch(self):
        ...

    async def __aiter__(self):
        return self

    async def __anext__(self, fut):
        if not self.buffer:
            self.buffer = await self._prefetch()
        if self.buffer:
            fut.set_result(self.buffer.popleft())
        else:
            fut.set_exception(StopIteration)

While this is elegant, my concern is that one-future-per-iteration-step might 
be bad for performance.

Maybe consider the following. The `async def` syntax decouples the concept of a 
coroutine from the implementation. While it’s still based on generators under 
the hood, the user no longer considers his “async function” to be a generator 
or conforming to the generator protocol. From the user’s perpective, it’s 
obvious that the return below means something different than the exception:

    async def __anext__(self):
        if not self.buffer:
            self.buffer = await self._prefetch()
            if not self.buffer:
                raise StopIteration
        return self.buffer.popleft()

So, the same way we added wrapping in RuntimeErrors for generators in PEP 479, 
we might add transparent wrapping in a _StopAsyncIteration for CO_COROUTINE.

> 7. compatibility with asyncio and existing users of it

+1, this is really important.

-- 
Best regards,
Łukasz Langa

WWW: http://lukasz.langa.pl/
Twitter: @llanga
IRC: ambv on #python-dev

_______________________________________________
Python-Dev mailing list
Python-Dev@python.org
https://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: 
https://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com

Reply via email to