What I'm trying to do
Given the following Node:
class Node<T> {
Node(this.value);
T value;
Node? child;
// TODO: add `visit` method
@override
String toString() => value.toString();
}
I'd like to add a visit
method that will perform some action on the value of each node and its child recursively. Then I could do something like this:
void main() {
final root = Node(1);
root.child = Node(2);
root.child!.child = Node(3);
// one of these
root.visit(print);
root.visit((value) => print(value));
// 1
// 2
// 3
}
Naive solution
If I do the following, it works:
void visit(Function action) {
action(value);
child?.visit(action);
}
Problems with the naive solution
However, the value
in this statement is inferred to be dynamic
:
root.visit((value) => print(value));
I'd like to infer it to be the same type as the Node's generic T
type.
Additionally, the compiler allows the following, which causes a runtime crash:
root.visit(() => 42);
I'd like that to be a compile-time error.
Attempted solution 1
If I change visit
to the following:
void visit(Function(T value) action) {
action(value);
child?.visit(action(value));
}
Everything looks good at compiletime:
root.visit(print); // OK
root.visit((value) => print(value)); // OK
root.visit(() => 42); // error
But if I comment out that last one and run the code on either of the first two then I'll get the following runtime error:
Unhandled exception:
type 'Null' is not a subtype of type '(dynamic) => dynamic'
I'm not exactly sure what that means.
Attempted solution 2
Added void
:
void visit(void Function(T value) action) {
action(value);
child?.visit(action(value)); // error
}
This expression has a type of 'void' so its value can't be used. Try checking to see if you're using the correct API; there might be a function or call that returns void you didn't expect. Also check type parameters and variables which might also be void. (dartuse_of_void_result)
Attempted solution 3
This one was just a stab in the dark:
void visit(void Function<T>(T value) action) {
action(value);
child?.visit(action);
}
The visit
method seems to compile but calling it as before gives compile time errors:
root.visit(print); // error
root.visit((value) => print(value)); // error
The errors read:
The argument type 'void Function(Object?)' can't be assigned to the parameter type 'void Function(T)'. (dartargument_type_not_assignable)
Related questions
These questions seem related but I couldn't figure out how to extract a solution from them:
- How to create a generic method in Dart?
- Callback with generic type parameter in Dart
- Dart: Type error when overrding generic function in abstract class
- How to check and cast generic parameters in Dart?
- Dart passing generic Function<T>(T t) seems to require cast, all other ways signatures don't match
How can I solve the problem?
CodePudding user response:
Thank you to @jamesdlin in the comments for solving the problem.
You need to set the generic type for the child
as Node<T>
. Then you can specify the method signature as void visit(Function(T value) action)
and pass action
itself on to the child.
Here is the full example:
void main() {
final root = Node(1);
root.child = Node(2);
root.child!.child = Node(3);
// one of these
root.visit(print);
root.visit((value) => print(value)); // value inferred as int
// root.visit(() => 42); // compile-time error
// 1
// 2
// 3
}
class Node<T> {
Node(this.value);
T value;
Node<T>? child;
void visit(Function(T value) action) {
action(value);
child?.visit(action);
}
@override
String toString() => value.toString();
}