what I mean... when I write code in Flutter (Dart) every time I have to call some callbacks sometimes they are: (_){} while other times they are: (_) => {} .
With or without parameters _ present.
To my knowledge, they should both be called lamda functions; and lambda functions should be other kind of write funtions
(_){} has signature Null Function
(_) => {} has signature Map Function() and it is short form (lambda) for
() {
return {};
}
This variant returns Map.
Full example (dartpad):
typedef SimpleFun = Null Function();
typedef LambdaFun = Map Function();
void main() {
final SimpleFun simpleFun = (){};
final LambdaFun lambdaFun = ()=>{};
print(simpleFun()); // null
print(lambdaFun()); // {}
}
I hope this helps:
(_) => _ is a lamba expression.
A lambda function is a small function containing a single expression. It is used to perform a specific task and it can contain only that one expression
While
(_) {} is a function that can take as many expressions as you want to give it.
Hope this answers your question.
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");
Background:
I have been studying how to pass functions using a Map using dart code. However, I am now stumped. I am getting an unexpected null value from the following code when using DartPad when using null safety:
void main() {
Map<String, Function> fruits = Map();
fruits['apple'] = appleDescription;
fruits['banana'] = bananaDescription;
fruits['grape'] = grapeDescription;
exec(fruits['grape']!);
}
void appleDescription() => print('This fruit tastes like red!');
void bananaDescription() => print('This fruit tastes like yellow!');
void grapeDescription() => print('This fruit tastes like purple!');
void exec(Function f) {
print(f());
}
The DartPad Console is shown in the picture below:
Questions:
I reckon that the answer will be easy, but I have been earnestly struggling with this for some time now. My questions are:
I expected that only, 'This fruit tastes like purple!' to have been printed in the Console, so I must be missing something here. Am I passing this function correctly from the map, or is there a null-safer way of passing it?
I wondered why I must use a bang operator when calling the exec() function. Since I have defined that the fruits map contain <String, Function>, the compiler would understand that it must exist. What am I missing?
Again, thank you in advance for any advice is gratefully accepted from the community.
Update:
I used the following code to remove the bang operator with the corrections given in the answer below:
void main() {
Map<String, Function> fruits = Map();
fruits['apple'] = appleDescription;
fruits['banana'] = bananaDescription;
fruits['cranberry'] = grapeDescription;
exec(fruits['cranberry']??= (){print('');});
}
void appleDescription() => print('This fruit tastes like red!');
void bananaDescription() => print('This fruit tastes like yellow!');
void grapeDescription() => print('This fruit tastes like purple!');
void exec(Function f) {
f();
}
Your exec function tries to print the result of the f() function which itselfs prints the phrase you're waiting for.
So basically :
the code enters exec
calls f()
f() prints This fruit tastes like purple! and returns void
the print method in exec print the return value => null
To summarize, you are printing the return value of a print function.
For your code to act as you expect, you should use
void exec(Function f) {
f();
}
To reply to your second question, a Map can return null if you don't pass an existing key to it. That's why you should use the bang, because the return of the Map cannot be guaranteed to be non null.
I know about the extension feature in Dart, but how can I use it with functions?
Essentially, I am facing two problems:
What do I extend on (extension FancyFunction on ?)?
I would want to add a function like toAsync that makes the function return a Future of its usual result.
How would I implement calling?
I could create a callAsync member that executes this() or this.call(), but I would like to use regular calling syntax, i.e. just parentheses.
What do I extend on when extending functions?
Dart has a Function type. This can be extended on and you can pass type parameters if you want.
Here is an example from the changelog:
extension CurryFunction<R, S, T> on R Function(S, T) { ... }
Furthermore, you can extend any typedef of a function.
For adding the toAsync and callAsync functionality a generic return type R will do. Note that this will only extend functions without parameters as Function() takes no parameters:
extension FancyFunction<R> on R Function() {
Future<R> Function() toAsync() => () async => this();
}
Now, this could be used like this:
void syncHello() => print('Hello');
void main() {
final asyncHello = syncHello.toAsync();
asyncHello().then((_) => print(', World!'));
}
How do I implement calling?
Every class in Dart can implement the call method. You can either execute this method simply using parentheses or with .call().
Here is an example implementation:
extension FancyFunction<R> on R Function() {
Future<R> call() async => this();
}
Since every Function already implements call, the extension member cannot be called implicitly.
Instead, you will have to explicitly declare your function as a FancyFunction to be able to call it:
void syncHello() => print('Hello');
void main() {
FancyFunction(syncHello)()
.then((_) => print(', World!'));
}
Note that FancyFunction(syncHello)() is the same method call as FancyFunction(syncHello).call().
However, now we have two problems:
We have to explicitly declare our function to be a FancyFunction, which somewhat defeats the purpose of having an extension.
The call method will always return a Future as the regular call method cannot be accessed anymore when we declare a FancyFunction.
Instead, adding a method like callAsync seems like a better solution:
extension FancyFunction<R> on R Function() {
Future<R> callAsync() async => this();
}
Now, callAsync can be used implicitly again:
void syncHello() => print('Hello');
void main() {
syncHello.callAsync()
.then((_) => print(', World!'));
syncHello(); // regular call
}
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 would like to know if I can call a function with name parameters using a map e.g.
void main()
{
Map a = {'m':'done'}; // Map with EXACTLY the same keys as slave named param.
slave(a);
}
void slave({String m:'not done'}) //Here I should have some type control
{
print(m); //should print done
}
the hack here is to not use kwargs but a Map or, if you care about types, some interfaced class (just like Json-obj), but wouldn't be more elegant to just have it accept map as kwars?
More, using this hack, optional kwargs would probably become a pain...
IMHO a possible implementation, if it does not exist yet, would be something like:
slave(kwargs = a)
e.g. Every function that accepts named param could silently accept a (Map) kwargs (or some other name) argument, if defined dart should, under the hood, take care of this logic: if the key in the Map are exactly the non optional ones, plus some of the optional ones, defined in the {} brackets, and of compatible types "go on".
You can use Function.apply to do something similar :
main() {
final a = new Map<Symbol, dynamic>();
a[const Symbol('m')] = 'done';
Function.apply(slave, [], a);
}
You can also extract an helper method to simplify the code :
main() {
final a = symbolizeKeys({'m':'done'});
Function.apply(slave, [], a);
}
Map<Symbol, dynamic> symbolizeKeys(Map<String, dynamic> map){
return map.map((k, v) => MapEntry(Symbol(k), v));
}
The answer of #alexandre-ardhuin is correct but is missing something : How to call a constructor as Function.
You have to use the property new after the Classname. Here's an example :
main() {
final a = new Map<Symbol, dynamic>();
Function.apply(MyClass.new, [], a);
}