I've found that when talking about async/await stuff recently, I've mostly dropped the word "coroutine" from my vocabulary and replaced it with "async function". I'm writing to suggest that we might want to make this switch as a community, and do it now, before the next 10x increase in the async/await userbase. Rationale:
Consistency: If the only two words to keep track of were function vs coroutine, that would be one thing. But here's a list of syntax constructs we have now, and how we talk about them in English:
with -> applies a context manager to a with block
async with -> applies an async context manager to an async with block
for -> does a loop over an iterator
async for -> does an async loop over an async iterator
def + yield -> makes a generator
async def + yield -> makes an async generator
[... for ...] -> is a comprehension
[... async for ...] -> is an async comprehension
def -> creates a function
async def -> creates a coroutine
One of these is not like the others. Why? It's a pointless toe-stub for newcomers.
Expressivity: Once I started talking about async functions, I've found my vocabulary suddenly expanding with useful new bonus terms, like "async method" (= an async def inside a class body), or "async callable" (= any callable that returns an awaitable, emphasizing the duck type rather than the concrete type).
Approachability: What is a "coroutine" anyway? Is it like a monad? Am I going to have to learn about endofunctors and continuations next? Of course we've all gotten used to the term now so we don't notice, but for a new user this is an obviously jargony "if you don't have a PhD then this isn't *for* you" kind of word. It's not hospitable.
Plus, it emphasizes the implementor under-the-hood point of view, not the user point of view. (Which is totally understandable! Because we're just now transitioning from the internal implementors hacking things out phase to the broad general user base phase, but that's why we should fix this now.) For an end-user, especially one starting out, they don't need to know or care about all the coroutine stuff that async/await is using to make the magic happen, they can learn that later. To get started, all you need to know is that you do async stuff by using async functions and sticking an 'await' onto your I/O calls. A lot of work went into abstracting away that magic and giving it a friendly Python syntax; the English syntax should follow suit.
Accuracy: Speaking of jargon, the term "coroutine" *is* an existing piece of jargon in computer science, and our term and their term don't quite match up. This isn't a huge deal, but it's unnecessary confusion. According to Wikipedia I guess technically we don't even have "true" coroutines, just "semicoroutines"? And every generator has just as much claim to being a coroutine-in-the-CS-sense as an async function does, but when we say coroutine we don't mean generators. (Except when we do.) This confusion might partly reflect the somewhat confusing transition from 'yield from' to async/await, as demonstrated by the official doc's somewhat confusing definition of "coroutine":
https://docs.python.org/3/library/asyncio-task.html#coroutine
But going forward, yield-from-based coroutines and @asyncio.coroutine and all that are quickly becoming a historical curiosity, and most of the time what we really want to be talking about are just async functions.
So: I'm not suggesting we modify Python itself to rename types.coroutine or the .cr_* fields or anything, that's all expert internal stuff (possible exception: the __repr__ for async functions). But I do propose that we all agree to do a search/replace for s/coroutine/async function/g on our end-user docs and when generally talking about these things, and announce that we're doing so. It's a small thing and a bit of work, but if there's a general agreement to do it in a coordinated way then I think it could be pretty straightforward transition that will pay dividends for a long time.
-n
--
Nathaniel J. Smith -- https://vorpus.org