import 'dart:html';
main() {
new test();
}
class test {
test() {
print(x);
}
void x() {
window.requestAnimationFrame(x);
}
}
I could use animationFrame but I want more control so I try to use requestAnimationFrame (without futures). So I call the method from my window object and pass in the callback only to get the following error message:
The argument type '() → void' cannot be assigned to the parameter type 'FrameRequestCallback(num) → void'.
https://dartpad.dartlang.org/fb763a4a770b5cdd896982e10ccf4118
According to dart my x variable is a closure of instance test, great. It cant be void then.
The documentation clearly states the function accepts a callback of type FrameRequestCallback (the method I assume). The function then returns an integer / num. Why doesnt my test project work?
https://api.dartlang.org/stable/1.21.1/dart-html/FrameRequestCallback.html shows that a param of type num is expected.
This should work then
void x(num value) {
window.requestAnimationFrame(x);
}
Related
I'm using Dart 2.18.0 now. Here's what the docs say about
the entry of Isolate.spawn.
The function must be a top-level function or a static method that can be called with a single argument, that is, a compile-time constant function value which accepts at least one positional parameter and has at most one required positional parameter. The function may accept any number of optional parameters, as long as it can be called with just a single argument. The function must not be the value of a function expression or an instance method tear-off.
Here's the code I write,
import 'dart:io';
import 'dart:isolate';
void main(List<String> args) {
Isolate.spawn((message) {
print("$message ${Isolate.current.debugName}");
}, "Hello world!", debugName: "block");
final p = Person();
Isolate.spawn(p.saySomething, "Nice to meet you!",
debugName: "Object method");
Isolate.spawn(
p.saySomething2(), "A function from the value of a function expression!",
debugName: "Function Expression");
sleep(Duration(seconds: 1));
}
class Person {
void saySomething(String something) {
print("$something ${Isolate.current.debugName}");
}
void Function(String message) saySomething2() {
return saySomething;
}
}
See, I pass a block and a method of a instance to each Isolate.spawn. The code runs well and it prints the following logs.
Hello world! block
Nice to meet you! Object method
A function from the value of a function expression! Function Expression
But how? Block is neither a top level function nor a static function. The function from a value of a function expression and the method of an instance can also be passed as Isolate entry.
Do I get it wrong? Or these features has been supported, the doc has not updated yet?
I think the problem here is that you're passing a function as a parameter, when
Isolate.spawn
expects a function expression.
The docs say:
The function must be a top-level function or a static method that can be called with a single argument, that is, a compile-time constant function value which accepts at least one positional parameter and has at most one required positional parameter. The function may accept any number of optional parameters, as long as it can be called with just a single argument. The function must not be the value of a function expression or an instance method tear-off.
You can see an example of a function expression in the docs here:
int foo(int a, int b) => a + b;
Your
saySomething2
Function returns a function expression, so you can use that directly:
Isolate.spawn(p.saySomething2(), "A function from the value of a function expression!", debugName: "Function Expression");
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?
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();
}
Consider the following code:
void printInt(int i) => print(i);
void printString(String s) => print(s);
void printSomething(Object o) {
final printer = {
int: printInt,
String: printString,
}[o.runtimeType];
print('Printer is $printer');
printer(o);
}
void main() => printSomething('Hello');
It prints the correct printString function and then crashes with the following exception:
TypeError: "Hello": type 'String' is not a subtype of type 'Null'
Why does that happen?
The error comes from the fact that your map has been given the type Map<Type, void Function(Null)> since that is the only type it can use based on the content of you list.
The problem is that Dart cannot give your map another type since anything else would not be valid from the perspective of the type system. Let's say the type was Map<Type, void Function(Object)>. Well, we are then allowed to send any object into a method from this map. But that is not allowed since your two methods in the map clearly are defined as accepting int and String and not Object.
We can either give it the type Map<Type, void Function(int)> since we have a method taking a String.
Also, Map<Type, void Function(dynamic)> has the same problem as Object since both methods are clearly defined to taking a precise type.
So Dart will instead use Null as the type of the parameter since the only thing we know is valid to give both methods are the null value.
And since you are then trying to give a String as parameter to a method with the signature of void Function(Null) you are getting an error from the type system.
If you want you code to run, you need to tell the type system to keep quite about what you are doing. To do that, you should use dynamic for the type of method you are receiving from the map:
void printInt(int i) => print(i);
void printString(String s) => print(s);
void printSomething(Object o) {
final dynamic printer = {
int: printInt,
String: printString,
}[o.runtimeType];
print('Printer is $printer');
printer(o);
}
void main() => printSomething('Hello');
I am using Dart2
What I want to do is to force developer to use callback that accepts no or at most 1 argument.
For example, having following method:
void doYourJob(void onComplete([result])){ //this is what I have tried, buts its wrong - requires ([arg])=> callback
.... do your job
onComplete(result);
}
I would like to be able to use that method in two ways eg:
doYourJob(()=>doStuff);
and
doYourJob((result)=>doMoreStuffWithResult(result));
Is it possible to do something like this?
No. What you are trying to do is not possible.
You want to have a function type which accepts functions taking either zero or one argument. That is, you want to be able to pass a unary function and a nullary function.
That is, a function type which is a supertype of both void Function() and void Function(Object).
That is not the same as a function type with an optional parameter. Such a function type requires that all arguments must be callable both with zero and one argument. You cannot pass a pure unary function to that, because that function cannot be called with zero arguments.
There is no function type which is a supertype of both void Function() and void Function(Object). The nearest supertype is Function, which accepts any function, not just unary and nullary ones. You can use that, but you lose the type checking.
You can do this by creating a typedef
typedef Callback = Null Function([String data]);
void doYourJob(Callback onComplete) {
onComplete('Data');
onComplete();
}
OR
You can pass the Function directly
void doYourJob(Null Function([String data]) onComplete) {
onComplete('Data');
onComplete();
}
You can call this method like this
void main() {
doYourJob(([String data]) {
print('DATA: $data');
});
}
I'm reasonably proficient with Groovy insofar as my job requires, but not having a background in OOP means that some things still elude me, so apologies if some of the wording is a little off here (feel free to edit if you can make the question clearer).
I'm trying to create an overloaded method where the signature (ideally) differs only in the return type of the single Closure parameter. The Closure contains a method call that returns either an ItemResponse or ListResponse object, both of which could contain an object/objects of any type (which is the type I would like to infer).
The following code is a simplified version of what I'm trying to implement - an error handling method which takes a reference to a service call, safely attempts to resolve it, and returns the item/items from the response as appropriate:
public <T> T testMethod(Closure<ItemResponse<T>> testCall) {
testCall.call().item as T
}
public <T> List<T> testMethod(Closure<ListResponse<T>> testCall) {
testCall.call().items as T
}
Obviously this doesn't work, but is there any alternate approach/workaround that would achieve the desired outcome?
I'm trying to create an overloaded method where the signature
(ideally) differs only in the return type of the single Closure
parameter.
You cannot do that because the return type is not part of the method signature. For example, the following is not valid:
class Demo {
int doit() {}
String doit() {}
}
As mentioned by yourself and #jeffscottbrown, you can't have two methods with the same parameters but different return value. The workaround I can see here is to use a call-back closure. The return value of your testMethod would default to Object and you would provide an "unwrapper" that would the bit after the closure call (extract item or items). Try this out in your GroovyConsole:
class ValueHolder <T> {
T value
}
Closure<List<Integer>> c = {
[1]
}
Closure<ValueHolder<String>> d = {
new ValueHolder(value:'hello world')
}
Closure liu = {List l ->
l.first()
}
Closure vhsu = {ValueHolder vh ->
vh.value
}
// this is the generic method
public <T> Object testMethod(Closure<T> testCall, Closure<T> unwrapper) {
unwrapper(testCall.call()) as T
}
println testMethod(c, liu)
println testMethod(d, vhsu)
It works with both a list or a value holder.