Skip to content

Make "void" arguments syntactically optional? #34176

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

Open
MichaelRFairhurst opened this issue Aug 17, 2018 · 5 comments
Open

Make "void" arguments syntactically optional? #34176

MichaelRFairhurst opened this issue Aug 17, 2018 · 5 comments
Labels
area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). language-discussion

Comments

@MichaelRFairhurst
Copy link
Contributor

Instead of

Future<void>().then((_) { ... });
Completer<void>().complete(null);
Stream.listen((_) { ... });
new StreamController().add(null);

Could we make it possible to write just

Future<void>().then(() { ... });
Completer<void>().complete();
Stream.listen(() { ... });
new StreamController().add();

?

Some tendrils I can think of here:

  • Does this mean void Function(void) is a subtype of void Function() ? (thought: no, because its not ABI compatible)
  • If not, how can () -> R be assignable to (T) -> R in Future.then?
  • If () {} is inferred as (void) -> R, is there a way to write this explicitly? Is (void _) {} close enough?
  • won't handle Function(..., void, ..., T)
  • cardinality of functions becomes no longer a single answer (Function(void, void, void) would have four different cardinalities -- everything from 0 to 3)
  • adding a new way of doing the same thing is not as good as having one excellent method. But I don't think this could replace the alternative
@ds84182
Copy link
Contributor

ds84182 commented Aug 17, 2018

This would be great since void doesn't have a concrete value (by design, unlike kotlin's Unit type which is more like Null). Keeping the value out of the equation makes using async for signals/completion much nicer.

Something I just thought about: Would yield null; become just yield; in sync* and async* functions?

@MichaelRFairhurst
Copy link
Contributor Author

This would be great since void doesn't have a concrete value

Yeah, that's a good point. It's valid to throw away anything into a void location (such as StreamController<void>.add(...)). Using null here is just a convention, really. It would be nice if people didn't have to learn such a convention for these use cases (though arguably that's offset by needing to learn that the parameter is omittable?).

Regarding

Would yield null; become just yield; in sync* and async* functions

This is actually already true! yield; and return; are syntactic sugar for yield null; and return null;. However, it's not really usable like that.

To be "optionally typed," and yet still have some type of safety, we made it a compile-time error to mix both "return;" and "return exp;" in one function body. (same with yield). It would maybe be useful to allow this in the Dart 2 at some point. It's arguably still suspect behavior, but maybe if we had non-nullable types, we could allow a mixture when the return type is a nullable one.

@vsmenon vsmenon added the area-language Dart language related items (some items might be better tracked at github.com/dart-lang/language). label Aug 21, 2018
@avilladsen
Copy link

I would find this more useful if it also worked for named functions. A fairly common situation for me is that I have a function void foo() { ... } that I use in several places, including something like Future<void>().then(() { foo(); }). That's harder to mentally parse as "and then do foo" than Future<void>().then(foo) is. I could change foo to void foo(void _) { ... }, but then it would be strange to have to call foo(null) in the other places I use it.

However, I don't see how this could work well as just syntax sugar. As sugar, I think it would have to be some "rewrite rules" based on the expected type at a position to e.g. rewrite Future<void>.then(() { ... }) as Future<void>.then((void _) { ... }), or for named functions rewrite Future<void>.then(foo) as Future<void>.then((void _) { foo(); }). Both of those introduce surprising or inconsistent behaviors:

// Does not work if you only do anonymous function rewriting
var x = () {};
Future<void>.then(x);
// Does not work (i.e. returns null) in either case,
// since the rewrite rules depend on static typing
Future<void> bar(callback) {
  if (callback is void Function(void)) {
    return Future<void>().then(callback);
  }
  return null;
}
bar(() {});
// Implicitly wrapping named functions changes the semantics of function equality,
// which could be a nasty surprise if you're registering and deregistering callbacks
var nullaryFuncs = Set<void Function()>();
var unaryFuncs = Set<void Function(void)>();

void foo() {}
nullaryFuncs.add(foo);
nullaryFuncs.add(foo);
unaryFuncs.add(foo);
unaryFuncs.add(foo);

// as expected:
assert(nullaryFuncs.length == 1);
nullaryFuncs.remove(foo);
assert(nullaryFuncs.isEmpty);

// but surprisingly:
assert(unaryFuncs.length == 2); 
unaryFuncs.remove(foo);
assert(unaryFuncs.isNotEmpty);

To avoid these issues I think you would have to make void Function(void) a subtype of void Function(), and pass around the original function without creating a wrapper. Though as you mentioned they're not ABI compatible, so there'd be a runtime cost for checking and adapting the parameters every time a function value is called. That plus the fact that it would be a breaking change makes it not worth the convenience in my opinion.

@cedvdb
Copy link
Contributor

cedvdb commented Sep 3, 2022

Shouldn't this be in the language repository ?

@MichaelRFairhurst
Copy link
Contributor Author

MichaelRFairhurst commented Sep 4, 2022 via email

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-discussion
Projects
None yet
Development

No branches or pull requests

5 participants