Async / Await syntax straw poll

I updated the above post to include this info. (Some number slightly changed and were updated)

1 Like
  • Keyword (prefix) - 65.4% approve, 33% disapprove
  • Keyword with ? - 29.8% approve, 69.2% disapprove

It means 35% of voters approve

(await future)?

but disapprove

await? future

I wonder why. My theory is that await? option is not fully understood.

1 Like

See the paper writeup from the first thread for one possible reason:

The await? construct, proposed to resolve problematic interactions between a prefix await construct and the postfix ? operator, does not have this property of orthogonality. A user who has learned the concept and syntax of ? and of await cannot immediately understand what await? means. await? introduces a new, special-case syntactic usage of ? that doesn’t directly follow from existing developer knowledge of await and ?. Furthermore, it isn’t immediately obvious from the syntax await? foo() whether the ? occurs before or after the await.

Similarly, what expectations might someone have from seeing await? foo()? Would await ?foo() work? (Probably, given our normal tokenizing rules.) Would await (?foo()) work? (Nope, probably not, because it’s not a prefix ?, it’s a special construct.) Would loop? { ... } work? (I suppose it could? I guess it’d mean loop {...}??)

2 Likes

Would await (?foo()) work?

Nope, because await? is a whole syntax construct, not a combination of ? and await.

Same way as if let is a “keyword”, not regular if and boolean let.

Another example is &mut. It is a whole syntax construct; & and mut cannot be interpreted separately. It means “create a mutable reference to data”, not “create a reference to mutable data”.

let a = &mut b;

is not the same as:

let a = &(mut b);

await? can be called try_await, but await? looks nicer.

Would loop? { ... } work?

It may or may not work, but loop? is a different syntax construct which can be discussed separately.

Similarly if let did not immediately enable while let.

A user who has learned the concept and syntax of ? and of await cannot immediately understand what await? means.

The same reasoning applies to postfix .await. A user who learned the concept of field access and futures cannot immediately understand why field await need to be accessed to suspend the execution of the current future.

That said, I doubt learnability of syntax is an issue. Because learning syntax is hundred times easier than learning underlying semantics (what happens when async is used).

Thank you for the explanation. Not sure it is the whole reason, but at least arguments against prefix await make sense.

1 Like

This is a bad analogy because if let ps = e b (else b)? is soon becoming the combination of if e b (else b)? and let ps = e. In other words, syntactically speaking, let ps = e is becoming an expression. Moreover, the current state of affairs, where if let is a separate construct, is not good. Specifically, the fact that you cannot say if p && let qs = r { .. } makes code read quite poorly in the compiler. The moral of this story is that constructs that don’t compose are unfortunate and should be avoided when possible.

This is a good example of when it isn’t so easy to make syntax composable because fundamentally the semantic construct is not a composition. await? is not one of those cases. await? is a composition of constructs and it is easy to give it composable syntax.

Is there a TryFuture trait which this operator await? is linked to? Importantly, if let was and is, for perhaps a few more weeks, a separate semantic construct than if. However, await? fut and (await fut)? would be equivalent.

There isn’t really a good reason for this aside from “we didn’t have time or experience”, and the former rather quickly begat the latter.

3 Likes

However, await? fut and (await fut)? would be equivalent.

Is there a TryFuture trait which this operator await? is linked to?

There is none. But the same way there is nothing special about ? operator, it is just syntax sugar to

match expr {
    Ok(r) => r,
    Err(e) => return Err(e.into()),
}

So technically ? is redundant.

So await expr can be a native operation, while await? expr can be desugared to (await expr)?.

The moral of this story is that constructs that compose are unfortunate and should be avoided when possible .

I agree with these arguments.

It’s just I think general syntax consistency/symmetry (keywords are almost always prefixed, e. g. do not have x if x >= 0 else -x or expr.match { ... }) overweights the redundancy of await? operator.

BTW, I just realized that poll done by @tkaitchuck has no intrinsic function option. Which is

fut.await()?;

where await is not a keyword, but an intrinsic function in Awaitable trait. This might be a compromise for everybody: it allows chaining, it is composable with ? and it does not break core language syntax symmetry.

1 Like

Incorrect. ? uses the Try trait, and behavior is customized by using that trait:

match Try::into_result($expr) {
    Ok(ok) => ok,
    Err(err) => return Err(From::from(err)),
}

Your clarification does not contradict my argument that ? is just syntax sugar and so could be await?.

Beside @CAD97’s point, it seems clear to me that e?, lhs && rhs, lhs || rhs, if c { bi } else { be }, and so on, justify their existence by providing enough semantic compression to be worthwhile as syntactic sugars over match. This is unlike await? expr, which offers no compression over expr.await? (both are exactly of the same length), which does not add additional syntactic forms over expr.await and ? themselves.

5 Likes

It does, over (await e)?.

You are just wrong.

expr =
  | "await" expr
  | expr "?"
  | IDENT // Make our grammar terminating.
  | "(" expr ")"
  ;

vs.

expr =
  | expr "?"
  | expr "." "await"
  | IDENT
  ;

If we want to nitpick, the first grammar requires one more alternative (parenthesis) to express as much as the second one.

I didn’t say that adding both await and await? makes the grammar simpler. It doesn’t, as syntactic sugar never does. But it does provide the semantic compression you mentioned while arguing for e? and the others.

And Rust already has brackets and fields, so counting the productions in a subset of the grammar doesn’t prove anything.

1 Like

I thought you meant that (await e)? (without await? e) requires fewer syntactic forms than e.await? (not true).

Just new to this discussion, has a “.” + some sigil been mentioned already? For example:

let result = future.!await?;
let result = future.@await?;
let result = future.#await?;

extended to match syntax it will be like:

try {...}.@match {...}
1 Like

What do you like about the second sigil? It’s syntactically unnecessary, but does it have some other advantage?

What do you like about the second sigil? It’s syntactically unnecessary, but does it have some other advantage?

It just makes mentally and visually much easier to distinguish field access from await, because no field name starts with a sigil. And consider reviewing a diff patch, we don’t/can’t have syntax highlighting from IDE to help.

For this version of await I’d suggest to add a space before and after:

let a = get_result_of_future() await ?;
let a = get_result_of_future() await .or_else(|_e| Ok(...))?;
let a = get_result_of_future() await + 7;
1 Like

I get what you’re saying, but it still feels weird. At that point you might as well do something like:

let a = get_future().(await)?;
let a = get_future().(await).or_else(|_e| Ok(...))?;
let a = get_future().(await) + 7;

It seems unnecessary to me to replace .await with either of these. I don’t see the benefit as being particularly compelling. This might work for postfix function call syntax, though…

Final Results

The polls is now closed. You can see the results by clicking the link.

Summary: There have been no substantive percentage changes since my last update. As almost all of the votes were in at that point.

The final approval rank order was:

  1. Macro style - await!(future)
  2. Keyword Prefix - await future
  3. Method call style - future.await()
  4. Function call style - await(future)
  5. All the variation on keyword style (parenthesis, braces, tight binding, ?) in that order.
  6. Field access style - future.await
  7. Field! access style - future.await!
  8. Sigils @ followed by #.

When ranking by favorites it is almost the same except that Keyword (prefix) style comes in first. (by a fairly wide margin).

Observations

  • People do not want a new sigil. This initially was quite popular with early voters but dropped off rapidly with later voters.
  • The prefix style options: Prefix keyword or Macro style are strongly preferred to the postfix style that was supposed to improve upon them. This is reflected in both favorites as well as approval / disapproval percentages.
  • The extensions to the keyword syntax to attempt to improve it, such as adding a try keyword are much less popular than simply leaving the keyword alone.
  • The future.await syntax proposed by the lang team is very widely disapproved of. It’s approval percentage has not changed since it was announced as the proposed syntax. It has remained around 75% disapproval since the poll opened.

As noted before. This is not a vote and may well have no effect on the overall outcome. But is here to provide useful feedback to the developers.

5 Likes

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.