Open dtonhofer opened 6 months ago
More generally, there might be room for a section on "how to declare parameters that are typed as functions", as it is not immediately clear how to do that. Here is some example code:
// A procedure taking a function taking no arguments and returning anything
void bar1(dynamic Function() body) {
print('bar1() called with ${body.runtimeType}');
body();
}
// The same as above but with deprecated syntax. The linter will warn!
void bar2(dynamic body()) {
print('bar2() called with ${body.runtimeType}');
body();
}
// This also works: instead of "dynamic" return a nullable Object.
// However, in the case of "dynamic" the compiler will do type inference,
// but if you return "Object?" you actually fix the type of the returned
// value.
void bar3(Object? Function() body) {
print('bar3() called with ${body.runtimeType}');
body();
}
void callingBar() {
bar1(() => 6); // () => int
bar2(() => 6); // () => int
bar3(() => 6); // () => int
bar1(() => null); // () => Null (null is the single inhabitant of type Null)
bar1(() {}); // () => Null (even if the body does not return anything)
bar1(() {
return;
}); // () => Null (even if the body does not return anything)
}
// ---------------
// A procedure taking a function taking no arguments and returning void.
void foo1(void Function() body) {
print('foo1() called with ${body.runtimeType}');
body();
}
// The same as above but with deprecated syntax. The linter will warn.
void foo2(void body()) {
print('foo2() called with ${body.runtimeType}');
body();
}
void callingFoo() {
foo1(() => 6); // () => void
foo2(() => 6); // () => void
foo1(() {}); // () => void
foo1(() {
return;
}); // () => void
}
// linter warns: "don't return null from a function returning void"
void callingFooDubiously() {
foo1(() => null); // () => void
}
// ---------------
// A procedure taking a function taking no arguments and returning bool.
void baz1(bool Function() body) {
print('baz1() called with ${body.runtimeType}');
body();
}
// The same as above but with deprecated syntax. The linter will warn.
void baz2(bool body()) {
print('baz2() called with ${body.runtimeType}');
body();
}
void callingBaz() {
baz1(() => true); // () => bool
baz2(() => true); // () => bool
baz1(() {
return true;
}); // () => bool
}
// ---------------
// A procedure taking a function taking an int and returning bool.
void quux1(bool Function(int x) body) {
print('quux1() called with ${body.runtimeType}');
body(12);
}
// Same as above
void quux2(bool Function(int) body) {
print('quux2() called with ${body.runtimeType}');
body(12);
}
// The same as above but with deprecated syntax. The linter will warn.
void quux3(bool body(int x)) {
print('quux3() called with ${body.runtimeType}');
body(12);
}
// Not the same as above!! The token 'int' is not interpreted
// as a type: "body" takes a "dynamic", not an "int"!
void quux4(bool body(int)) {
print('quux4() called with ${body.runtimeType}');
body(12);
}
void callingQuux() {
quux1((x) => true); // (int) => bool
quux1((int x) => true); // (int) => bool
quux1((int x) {
// (int) => bool
return true;
});
quux2((x) => true); // (int) => bool
quux2((int x) => true); // (int) => bool
quux3((x) => true); // (int) => bool
quux3((int x) => true); // (int) => bool
quux4((x) => true); // (dynamic) => bool (UNEXPECTED!!)
// quux4((int x) => true); // Does not compile
}
// ---------------
// A procedure taking a function taking an int and a named String
// and returning bool.
void alf1(bool Function(int, {String z}) body) {
print('alf1() called with ${body.runtimeType}');
body(12, z: "cat");
}
// The same as above but with deprecated syntax. The linter will warn.
void alf2(bool body(int i, {String z})) {
print('alf2() called with ${body.runtimeType}');
body(12, z: "cat");
}
// Not the same as above! The token 'int' is not interpreted
// as a type: "body" takes a "dynamic" and a name String.
void alf3(bool body(int, {String z})) {
print('alf3() called with ${body.runtimeType}');
body("hello", z: "cat");
}
void callingAlf() {
alf1((x, {z = "hello"}) => true); // (int, {String z}) => bool
alf2((x, {z = "hello"}) => true); // (int, {String z}) => bool
alf3((x, {z = "hello"}) => true); // (dynamic, {String z}) => bool
}
// ---------------
// comparisons
void moo(bool Function(int x, {String z}) body1,
bool Function(int x, {String z}) body2) {
print('moo() called with ${body1.runtimeType} and ${body2.runtimeType}');
print('Are the types equal? ${body1.runtimeType == body2.runtimeType}');
}
void callingMoo() {
moo((x, {z = "hello"}) => true, (x, {z = "world"}) => false);
}
void woo(bool Function(int x, {String z}) body1,
bool Function(double x, {String z}) body2) {
print('woo() called with ${body1.runtimeType} and ${body2.runtimeType}');
print('Are the types equal? ${body1.runtimeType == body2.runtimeType}');
}
void callingWoo() {
woo((x, {z = "hello"}) => true, (x, {z = "world"}) => false);
}
void main() {
callingBar();
callingFoo();
callingFooDubiously();
callingBaz();
callingQuux();
callingAlf();
callingMoo();
callingWoo();
makeAdder(12);
print('${makeAdder(12)(11)}');
}
With the output:
bar1() called with () => int
bar2() called with () => int
bar3() called with () => int
bar1() called with () => Null
bar1() called with () => Null
bar1() called with () => Null
foo1() called with () => void
foo2() called with () => void
foo1() called with () => void
foo1() called with () => void
foo1() called with () => void
baz1() called with () => bool
baz2() called with () => bool
baz1() called with () => bool
quux1() called with (int) => bool
quux1() called with (int) => bool
quux1() called with (int) => bool
quux2() called with (int) => bool
quux2() called with (int) => bool
quux3() called with (int) => bool
quux3() called with (int) => bool
quux4() called with (dynamic) => bool
alf1() called with (int, {String z}) => bool
alf2() called with (int, {String z}) => bool
alf3() called with (dynamic, {String z}) => bool
moo() called with (int, {String z}) => bool and (int, {String z}) => bool
Are the types equal? true
woo() called with (int, {String z}) => bool and (double, {String z}) => bool
Are the types equal? false
Page URL
https://dart.dev/language/functions/
Page source
https://github.com/dart-lang/site-www/tree/main/src/content/language/functions.md
Describe the problem
At
https://dart.dev/language/functions#lexical-closures
we are shown a code snippet showing a higher-order function creating an "adder function".
I feel as if the typing part is getting a bit of a short shrift as the higher-order function
makeAdder()
has return typeFunction
, which is a bit general. (I discovered it can be modified to returndynamic
with good results, or else to returnObject
but then I can't do anything with the object, but that's just by the by).Suggesting to show an alternate
makeAdder2()
which further specifies what is being returned. Like this:Expected fix
No response
Additional context
No response
I would like to fix this problem.