Skip to content

Await void #28305

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
munificent opened this issue Jan 9, 2017 · 14 comments
Closed

Await void #28305

munificent opened this issue Jan 9, 2017 · 14 comments
Assignees
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). language-strong-mode-polish

Comments

@munificent
Copy link
Member

This code doesn't contain any static warnings:

void returnsVoid() {}

main() async {
  await returnsVoid();
}

Should it? It seems like code that is awaiting an immediate value is likely to be an error. Code that is awaiting void is really likely to be an error.

This cropped in internally because someone was doing:

await stuff.forEach((element) => convert it to a future...);

Instead of using forEach() they should have used map().

cc @floitschG @eernstg @lrhn @leafpetersen @bwilkerson

@munificent munificent added the area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). label Jan 9, 2017
@lrhn
Copy link
Member

lrhn commented Jan 9, 2017

I guess the static behavior of await is like FutureOr<T> -> T.
You can argue that it's unnecessary to catch non-futures, but it's also a good way to handle functions that return either a future or null.

That said, if the static type of the expression is void, then it should probably be a strong mode error - you are not allowed to "use" void results.

@munificent
Copy link
Member Author

You can argue that it's unnecessary to catch non-futures, but it's also a good way to handle functions that return either a future or null.

I think awaiting a non-future is likely to be an error, but not always one. It is useful to be able to write code that is "polymorphic over asynchrony". You have some object that may or may not be a future. It's handy to be able to reliably await that without having to check yourself to see if it's a future.

I don't think that makes sense with void, though. If some expression's static type is void, you should never get a future or a usable immediate value out of it.

That said, if the static type of the expression is void, then it should probably be a strong mode error - you are not allowed to "use" void results.

+1.

@zoechi
Copy link
Contributor

zoechi commented Jan 9, 2017

@leafpetersen
Copy link
Member

@lrhn is this captured by the category 1 or 3 errors that we have discussed?

@lrhn
Copy link
Member

lrhn commented May 13, 2017

This would be a category 1 error - an expression of type void in a position where its value is being used.

That said, I think I was actually considering allowing await (void expression) for some reason that I can't currently remember. We definitely need to allow await FutureOr<void>, and maybe that is what I was thinking of, because await (void expression) seems unnecessary by itself.

@eernstg
Copy link
Member

eernstg commented May 15, 2017

I think the consistent approach would be to allow await voidExpression as an expression statement (and in those few other cases where we decided to allow void expressions) based on the ability to use the same voidExpression on its own as an expression statement (etc.), and based on the fact that we allow things like await 3, even though it is statically known that 3 is an immediate (non-Future) value.

The lint that Günter mentioned could then optionally be used by developers to avoid all cases of "don't await an immediate", no matter whether it returns a useful value or not.

@lrhn
Copy link
Member

lrhn commented May 15, 2017

It's a case of "allow only the useful" vs. "disallow only the problematic", and we haven't decided exactly on which approach we'll favor.
An await (void expression) is neither useful nor problematic. There is no good reason to allow it, nor is there a good reason to disallow it. If nobody ever writes such an expression, the world will be no poorer.

I'm leaning towards disallowing it, although not heavily. Since its not useful, nobody will be hurt by that.
If anybody ever deliberately writes an await (void expression), it will most likely leave the reader confused, and if it's done by mistake, highlight it is probably a favor.

I don't buy "consistency" as an argument for that many things when it comes to void. The void type is an exception in almost every way, both behaviorally and intentionally.

@eernstg
Copy link
Member

eernstg commented May 15, 2017

OK, I'll try:

An await 23 is neither useful nor problematic. There is no good reason to allow it, nor
is there a good reason to disallow it. If nobody ever writes such an expression, the world
will be no poorer.

I'm leaning towards disallowing it, although not heavily. Since its not useful, nobody will be
hurt by that. If anybody ever deliberately writes an await 23, it will most likely leave the
reader confused, and if it's done by mistake, highlight it is probably a favor.

OK, it works! Then we should also ban await 23. And 24. ;-)

(or allow both the value- and the void-variant, of course)

@lrhn
Copy link
Member

lrhn commented May 15, 2017

To be honest, I tend to agree.
In strong mode/Dart 2, I wouldn't mind if require the static type of e in await e to, at least potentially, be a Future. That means assignable to some Future or being FutureOr of something. Just being int is right out.

In Dart 1, we don't generally let types decide program validity, so it would at most be a warning.

@matanlurey
Copy link
Contributor

In strong mode/Dart 2, I wouldn't mind if require the static type of e in await e to, at least potentially, be a Future.

+1

@leafpetersen
Copy link
Member

cc @natebosch @matanlurey

@lrhn @eernstg Is await void specified to be an error in the current void informal spec? Should it be?

@Crazywater
Copy link

Crazywater commented Jun 6, 2018

Note that await voidExpression can actually wait for something, which was very surprising to me:

import 'dart:async';

void main() async {
  print("a");
  await wait(); // waits 5 seconds
  print("b");
}

void wait() async {
  await Future.delayed(Duration(seconds: 5));
}

@eernstg
Copy link
Member

eernstg commented Jun 10, 2018

Is await void specified to be an error in the current void informal spec?

Yes, we don't have any items on the positive list matching await e which allows that construct with an e of type void, so that makes it a compile-time error.

This makes sense, too, because the expression could evaluate to a value of type Future<T> for some T. We will then perform a non-trivial computation with the value obtained from a void expression, and that's what we have otherwise made an error. Btw, this is exactly the situation with await wait() in the previous comment.

Given that we have explicitly stated (in the feature spec) that void f(...) async ... is intended to be a "fire-and-forget" function, it would also be a violation of that intention to silently allow the caller to await the returned future, that is, "to not forget".

@leafpetersen
Copy link
Member

#33415

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). language-strong-mode-polish
Projects
None yet
Development

No branches or pull requests

7 participants