Dart
  • Get Started
    • Get Dart
    • Dart Tutorials
    • Technical Overview
  • Docs
    • Programmer's Guide
    • API Reference
    • Dart Cookbook (Beta)
    • Dart: Up and Running
    • More Books
    • Articles
    • FAQ
  • Tools
    • Dart Editor
    • Pub Package Manager
    • More Tools
  • Resources
    • Code Samples
    • Translations from Dart
    • Try Dart!
    • Presentations
    • Dartisans Videos and Podcast
    • Dart Tips Videos
    • Bugs and Feature Requests
  • Community
    • Contact Us
    • Contributor's Guide
    • More Resources
  • Tweet
On Air

Emulating Functions in Dart

Written by Gilad Bracha
January 2012 (updated November 2012)

This document describes how to define Dart classes that behave like functions.

Contents

  1. The call() method
    1. How does it work?
  2. The apply() method
  3. Function types
  4. Interactions with noSuchMethod()
  5. Summary

The call() method

In the following example, we have an ordinary class WannabeFunction that happens to define a method named call().

class WannabeFunction {
  call(int a, int b) => a + b;
}

The call() method is special, in that anyone who defines a call() method is presumed to dynamically emulate a function. This allows us to use instances of WannabeFunction as if they were functions that take two integer arguments:

var wf = new WannabeFunction();
wf(3, 4); // 7

The example above is rather trivial, and we would be better off writing a function directly. However, there are cases where this ability can be quite useful. It is also core to the design philosophy of the Dart language:

  • What matters about an object is its behavior. If object a has a procedural interface that is compatible with that of another object b, a may substitute for b.
  • The interface of any kind of object can always be emulated by another suitably defined object.

How does it work?

When x(a1, .., an) is evaluated, if it is a normal function, it gets called in the normal way. If it isn’t we just invoke call() on it. If x supports a call() method with suitable arguments it gets called.

Otherwise, noSuchMethod() gets invoked. The default implementation of noSuchMethod() checks to see whether it was invoked due to an attempt to use call(), and if so issues a helpful error message suggesting you might have wanted to use a closure.

The apply() method

The class Function defines the static method apply() with the following signature:

external static apply(Function function,
                      List positionalArguments,
                      [Map<String, dynamic> namedArguments]);

The apply() function allows functions to be called in generic fashion.

Function types

An additional issue is how user-defined function classes relate to the type system. To simulate functions properly, we want them to be members of the appropriate function type:

typedef BinaryFunction(a,b);
...
new WannabeFunction() is BinaryFunction; // true

Therefore, we decree that an object is a member of a function type if the object’s class has a call() method and that method is a member of the function type.

Interactions with noSuchMethod()

In Dart, you can customize how objects react to methods that are not explicitly defined in their class chain by overriding noSuchMethod(). Here’s an example showing how you could use function emulation inside noSuchMethod():

noSuchMethod(InvocationMirror msg) =>
    msg.memberName == 'foo' ? msg.invokeOn(bar())
                            : Function.apply(baz,
                                msg.positionalArguments,
                                msg.namedArguments);

In the second line, invokeOn() handles the common case where you want to forward the call to a particular object (in this case, the result of bar()). The remaining lines handle the case where you want to forward just the parameters to another function. If you know baz doesn’t take any named arguments, then that code can instead be Function.apply(baz, msg.positionalArguments).

The only argument to noSuchMethod() is an InvocationMirror.

The boolean properties of InvocationMirror identify the syntactic form of the method invocation, as the following table shows.

  Form of method invocation
 x.yx.y = ex.y(...)
isMethod false false true
isGetter true false false
isSetter false true false
isAccessor true true false

It is important not to assume that isMethod means that a non-accessor was being looked up, since in fact, Dart semantics mean that we would have called noSuchMethod() only if neither a normal method nor a getter were found. Likewise, isGetter does not imply a getter was being looked up; if a method was present, it would be closurized and returned.

Summary

This document describes how to define Dart classes that behave like functions. This functionality will be supported in Dart starting with the M2 release.

Here is what you’ll need to know in order to implement your own function type in Dart once the feature is implemented:

  1. Define a class with a method named call.
  2. Implement the call() method to define what instances of your class do when invoked as functions via the () syntax.
  3. As a matter of good style, have the class implement the Function interface.

Popular on this site

  • Web UI
  • Performance
  • Language tour & library tour
  • Code samples
  • Tutorials
  • Cookbook

More resources

  • Try Dart!
  • Translations from Dart
  • Dart bugs and feature requests
  • Pub packages

Community

  • Mailing lists
  • G+ community
  • G+ announcement group
  • Stack Overflow

Dart is an open-source project with contributors from Google and elsewhere.

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 3.0 License, and code samples are licensed under the BSD License.

Terms of Service — Privacy Policy