Document Number: | P0071R00 |
---|---|
Date: | 2015-09-12 |
Project: | Programming Language C++, Evolution |
Revises: | none |
Reply to: | gorn@microsoft.com |
One of the question raised in Lenexa was: "Can we take a yield
identifier and make it a C++ keyword?". The answer was resounding no. There was
some ad-hoc brainstorming during the session on possible alternatives. This
paper examines the alternatives and makes recommendations.
We believe that in the world where we could have any keywords we would like,
our choice would be await
and yield
, as they most
clearly capture the notion of await
-ing on something or
yield
-ing a value to the consumer.
future<int> deep_thought() { generator<char> hello() {
await 5ms; for(auto ch: "Hello, world")
return 42; yield ch;
} }
But, we don't live in an ideal world. Developers use nice concise words as names of functions, variables, classes, namespaces, etc. Can we keep nice keywords and don't break existing code?
Soft Keywords proposal (p0056r0)
introduces a new named entity into the language: soft keyword. Soft keywords are
predefined in std
namespace and participate in name lookup and name
hiding according to existing language rules with some exceptions listed in the
paper.
Making await
and yield
soft keywords, not only
allows to use yield
and await
in coroutines and don't
break existing code, it will also allow use of coroutines in software where
await
and yield
are names of existing functions or
classes, since soft keywords allow qualification, as in std::yield
.
What if yield
and await
are magic functions defined
in the std
namespace?
It will make the syntax a little bit
noisier, since we now always have to put parenthesis around the expression being
yielded or awaited upon.
auto conn = await Tcp::Connect("127.0.0.1", 1337);
auto bytesRead = await conn.Read(buf, sizeof(buf));
will become
auto conn = await(Tcp::Connect("127.0.0.1", 1337));
auto bytesRead = await(conn.Read(buf, sizeof(buf)));
If we go one step further and increase the magic by giving magic functions precedence and unary operator call syntax, we just reinvented the soft keywords described in the previous section.
Lowercase await
is relative rarely used identifier (as compared
to yield) and it has a suitable replacement, namely wait
, which
carries roughly the same meaning and spelling. Thus, while taking
await
as a keyword will break some software, having a closely
related replacement wait
will lessen the pain.
yield
is used more frequently and there is no obvious term to
replace it with.
For coroutines yield
means both yield a value as in
output the value, produce the value as well yield execution back to the
caller. Out of existing set of C++ keywords only one carries a meaning that
is at least somewhat close to output, that is export
. However this
usage of export
will be confusing as the export is used in the
modules (N4465) with different meaning.
module greeting;
export generator<char> hello() { // exporting function hello
for(auto ch: "Hello, world")
export ch; // yielding a character
}
On the plus side, it is short and it is already a keyword.
yieldexpr
and yield_expr
are sufficiently odd
identifiers and score no hits on github or codesearch.
generator<char> hello() {
for(auto ch: "Hello, world") {
yieldexpr ch; // similar to constexpr
yield_expr ch; // similar to const_cast
}
}
Instead of using a suffix to oddify await
and yield
we can look at having an oddification prefix, such as co_
or
co
as was suggested during Lenexa coroutine discussion.
Without the underscore, co
prefix leads to wrong visual parsing
as in coy-ield
and thus inferior to co_
. We also need
to make a choice if we want to mangle await
for symmetry.
future<int> deep_thought() { generator<char> hello() { auto xform(Stream& s) {
await 5ms; for(auto ch: "Hello, world") for await(auto x: s)
return 42; co_yield ch; co_yield make_pair(x,time());
} } }
auto operator await(future<void>&) { ... }
or
future<int> deep_thought() { generator<char> hello() { auto xform(Stream& s) {
co_await 5ms; for(auto ch: "Hello, world") for co_await(auto x: s)
return 42; co_yield ch; co_yield make_pair(x,time());
} } }
auto operator co_await(future<void>&) { ... }
Given that await
is used in more contexts, such as name of the
operator and an option for a range-based-for statement, keeping a short version
await
as opposed to oddification co_await
is
preferrable.
To save committee time on bikeshed, use some obviously bad name, such as
yield_placeholder
and defer decision aboug yield-keyword
until a more suitable alternative is discovered / invented.
Soft keyword or its less general version magic functions leads to the best
coroutine syntax. None of the other alternatives considered come close to the
beauty of the clean await
and yield
keywords. If at
all possible, our overwhelming preference is for selecting one of the first two
alternatives or defer the bikeshed using yield_placeholder
until
some better alternatives are invented or we hit a deadline that a keyword must
be picked or else the feature will not make the standard.
p0056r0: Soft Keywords (http://wg21.link/p0056r0)
N4465: A Module System for C++ (Revision 3) (http://wg21.link/n4465)