runtimeType of Lexical Closure - dart

Code:
void main(){
print(foo().runtimeType);
}
Function foo(){
return (){};
}
Output:
() => Null
Why is this Null and not void?

From dart official site:
All functions return a value. If no return value is specified, the statement return null; is implicitly appended to the function body.

Related

A value of type 'Future<bool>' can't be returned from the function because it has a return type of 'Future<void>'

I have:
Future<bool> foo() async => true;
This is allowed:
Future<void> bar() {
return foo(); // Works
}
but this is not:
Future<void> baz() async {
return foo(); // Error
}
In both bar and baz I'm returning a Future<bool>, but why first works but second fails?
Note: This question isn't about HOW to make it work but rather WHY one works and the other doesn't.
Dart has special rules for void returning functions: You are not allowed to return a value because it's declared as not returning any value, so actually trying to return a value is probably a mistake.
The same happens without Future:
void qux() {
return true; // Error, can't return value from void function
}
gives the error
A value of type 'bool' can't be returned from the function 'qux' because it has a return type of 'void'.
You are allowed to have return e; statements, but only if the type of e is void, Null or dynamic. (And you shouldn't even do that, those are just allowed in order to make existing code work.)
(A => e body is always allowed, because people like to use it as a shorthand for just { e; }, mainly because the formatter keeps it on one line. I still recommend using { e; } as body of a void function.)
Generalizing that to async functions with void future return types,
you are not allowed to return an actual value from Future<void> ... async function. So, the only things you are allowed to return are void, dynamic, Null, Future<void>, Future<dynamic>, and Future<Null>.
A Future<bool> is neither of those.
What you should write instead is:
Future<void> baz() async {
await foo();
}
As I have observed when I am running the same code
Future<bool> foo() async => true;
Future<void> bar() {
return foo(); // Works
}
Future<void> baz() async {
// return foo(); // Error
}
void main() {
print("direct =====${foo()}");
print("bar==========${bar()}");
print("bazz========${baz()}");
}
The response is
direct =====Instance of '_Future<bool>'
bar==========Instance of '_Future<bool>'
bazz========Instance of '_Future<void>'
this signifies that when the async keyword is not present it takes the return type of the returning value.
But of the bazz function it gives a quick fix to make the function
Future<Future<bool>>
so when async is added the function return type is taken as the main type
Future<bool> foo() async => true;
First Case
Future<void> bar() {
return foo(); // Works
}
For the code above, the return of Future<void> bar() can return foo(), because the bar() not return directly the value of foo(), so the bar() can deal with foo() without no problem.
But, in the second case:
Future<void> baz() async {
return foo(); // Error
}
The code above is return directly the generic type of foo() because the function of baz() include async tag, so it will be trouble with the baz() as Future<void>, which is we know that foo() as Future<bool>

Identifying a function with a return type of void in runtime

I have a function that accepts another function and its arguments as arguments. The return type of the function and its parameter types are unknown. I need to know in runtime if the argument function returns a value or if it is a void function.
functionHandler(Function fn, List<dynamic> args) {
var result = fn(args[0], args[1]);
if ( -- fn returns a value --)
doSomething();
else if ( -- fn is a void function --)
doSomethingElse();
}
I can't use fn.runtimeType because the the parameter types are unknown.
A void function will return null, but then I can't distinguish between a void function and a function that simply returned a null value.
Is there any way I can check in runtime if fn is a function with return type void?
A hacky way would be to use fn.runtimeType.toString().endsWith('=> void').
Since you seem to know that fn takes exactly two positional arguments, a better way would be to make functionHandler a generic function, fully qualify your Function type with parameterized types, and then check the type parameter for the return type:
dynamic functionHandler<ReturnType, ArgumentType1, ArgumentType2>(
ReturnType Function(ArgumentType1, ArgumentType2) fn,
List<dynamic> args,
) {
// `ReturnType == void` is a syntax error, so we must do a little
// dance to get its corresponding `Type` object.
Type getType<T>() => T;
if (ReturnType == getType<void>()) {
print('void');
} else {
print('non-void');
}
}
void main() {
// Test a `void` function.
void voidFunction(Object arg1, dynamic arg2) {}
functionHandler(voidFunction, []); // Prints: void
// Test a function that returns `null`.
Null nullFunction(int arg1, String? arg2) => null;
functionHandler(nullFunction, []); // Prints: non-void
// Test a function with optional arguments.
int intFunction(int arg1, [int? arg2, int? arg3]) => 0;
functionHandler(intFunction, []); // Prints: non-void
}

Return type of void async function in Dart

In Dart, if you have an async function that doesn't return anything, should it return Future<void> or simply void? Both seem to work, but why?
void foo() async {
print('foo');
}
Future<void> bar() async {
print('bar');
}
void main() async {
await foo();
await bar();
print('baz');
}
Compiles with no errors or warnings and prints
foo
bar
baz
In your code, both functions foo() and bar() are not returning anything so any function return type is valid for both functions eg:
void/T/Future<T>... foo() async {
print('foo');
}
Future<void>/T/Future<T>... bar() async {
print('bar');
}
and here,
await foo();
await bar();
await just waits till the execution of these async functions is complete. As there is no return type, await has no purpose here(redundant) so this is and should not be a compilation error.
The difference is that Future<void> gives you information of the execution of the function like when the execution is complete and it also allows you to specify what to do when the function is executed by using bar().then(...) and bar().whenComplete(...).
While foo() function return void and void does not hold as such any info like Future<void>. If you try to await bar() it will convert that Future object from Future<void> to void.
Future is just a container, with await returns the values once the async "tasks" are completed. Without await, Future objects give you information about the execution of the function from which they are returning.
Thanks to the other answers - they were a little unclear to me so I'm just going to add some clarifications after experimenting on DartPad:
It is never an error or even a warning! to not return a value from a function, irrespective of the return type. This madness is presumably inherited from Javascript.
If you don't return from a function it implicitly returns null. Except in these cases:
If the return type is void, it does not return a value (and using the result of the expression is a compiler error).
If the function is async:
If the return type is void, you cannot use the result of the function, however you can still await it but you cannot use the result of the await expression, or use any methods of Future.
If the return type is Future<void> it returns an instance of Future<void>. You can await it, and call methods of Future, e.g. .then((void v) { ... });. However you cannot use the result of await (because it is void).
If the return type is Future<T> it returns an instance of Future<T> that resolves to null.
So basically, if you want to allow callers to use Future methods, you need to annotate the return type as Future<void>. If you merely want them to be able to await the function, you only need void. However since you probably don't know in advance I suspect it is a good idea to always use Future<void>.
Here's an example that demonstrates the possibilities:
// Compilation error - functions marked async must have a
// return type assignable to 'Future'
// int int_async() async {
// }
void void_async() async {
}
Future<void> future_void_async() async {
}
Future<int> future_int_async() async {
}
int int_sync() {
}
void void_sync() {
}
Future<void> future_void_sync() {
}
Future<int> future_int_sync() {
}
void main() async {
// print('${void_async()}'); // Compilation error - expression has type void.
print('future_void_async: ${future_void_async()}');
print('future_int_async: ${future_int_async()}');
// print('${await future_void_async()}'); // Compilation error.
await future_void_async();
future_void_async().then((void v) {
print('ok');
});
await void_async();
// void_async().then((void v) { // Compilation error.
// print('ok');
// });
print('await future_int_async: ${await future_int_async()}');
print('int_sync: ${int_sync()}');
// print('${void_sync()}'); // Compilation error - expression has type void
print('future_void_sync: ${future_void_sync()}');
print('future_int_sync: ${future_int_sync()}');
}
It prints
future_void_async: Instance of '_Future<void>'
future_int_async: Instance of '_Future<int>'
ok
await future_int_async: null
int_sync: null
future_void_sync: null
future_int_sync: null
A Future is simply a representation of an Object that hasn't completed the underlying function, and thus is a "promise" for later use. When you use Future<void>, there's no Object to return anyways, so it doesn't matter whether you use void or Future<void>. (The function doesn't return anything, so the return type is a placeholder anyways.)
However...
Future<void> can be used for listeners, and to check when things are complete, as can every Future. This means that you can use Future's listeners to check for completion or errors in running your Future<void> async function, but not your void async function. Thus, in some cases, it's better to give Future<void> than void as the return type.

The Fat Arrow is syntactic sugar - but do not return

The Fat Arrow is syntactic sugar, like described here
But if I remove return in(look for: future.then(print); // << Removed return) then it complains about missing return?:
Hmm, or am I missing a "return-return" kind og thing...
import 'dart:async';
main() {
printsome();
print("After should be called first...");
}
Future<void> printsome() {
final future = delayedFunction();
future.then(print); // << Removed return
// You don't *have* to return the future here.
// But if you don't, callers can't await it.
}
const news = '<gathered news goes here>';
const oneSecond = Duration(seconds: 1);
Future<String> delayedFunction() =>
Future.delayed(oneSecond, () => news);
I get this warning:
[dart] This function has a return type of 'Future<void>', but doesn't end with a return statement. [missing_return]
The => expr implicitly returns the result of expr, so if you want to replace it with a function body { return expr; } you need to add return, otherwise null will be implicitly returned.
You declared a function with return type Future<void> and DartAnalyzer warns you that your function is not returning such a value.
You can either add async which makes the function implicitly return a Future
Future<void> printsome() async {
final result = await delayedFunction();
}
or if you don't want to use async you can just add return
Future<void> printsome() {
final future = delayedFunction();
return future.then(print); // return the Future returned by then()
}

Dart strong mode: error on anonymous function return

I get the following error.
The argument type 'dynamic' can't be assigned to the parameter type '() -> dynamic'
The example is:
outerFunc(somevar) {
return () {....}
}
anOtherFunction(func()) {....}
anOtherFunction(outerFunc('test'));
These occurs when I return an anonymous function, in strong mode with the analysis_options.yaml on.
strong-mode:
implicit-casts: false
outerFunc doesn't specify a return type, therefore dynamic is assumed.
You can create a typedef and use it as return type for outerFunc.
The function type can't be inferred from the return statement.
typedef dynamic F();
F outerFunc(somevar) {
return () {};
}
You can also write the function type in-line
dynamic Function() outerFunc(somevar) {
return () {};
}

Resources