2

I know about the extension feature in Dart, but how can I use it with functions?

Essentially, I am facing two problems:

  1. 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.

  2. 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.

creativecreatorormaybenot
  • 114,516
  • 58
  • 291
  • 402

1 Answers1

3

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
}
creativecreatorormaybenot
  • 114,516
  • 58
  • 291
  • 402