Expressions
Method references
Naming a function, method or labeled constructor without calling it yields an ordinary function value.
since 0.1.0-alpha.1linuxwindowswasm
Description
A callable reached with :: or . that is used as a value, not immediately called, is a first-class function value. It can be stored in a variable, passed as an argument, or kept in an array of function values.
var f = NS::fn; // a namespace function: its own signature
var g = User::FromName; // a labeled constructor: (string) => User
b.handler = Controller::Login; // an instance method, unbound: the receiver becomes the first parameter
var save = editor.save; // a bound method: the receiver is captured
menu.onKey(this.onKeyDown); // `this` may be captured directly
There are two kinds of reference:
- An unbound reference names a method through its type (
Counter::get2). The receiver is not part of the reference, so it becomes the first parameter of the function:Counter::get2, declared asint get2(int k), has the type(Counter, int) => int. - A bound reference names a method through an instance (
c.bump). It captures the receiver, and the receiver does not appear in the function type:c.bumphas the type() => void. It is equivalent to the lambda() => c.bump().
The type of a reference is an ordinary function type, (A, B) => R, so it fits any field, parameter or variable of that type and can be stored in containers such as Array<(int) => int>.
Unbound, bound and constructor references
interface IAnimal {
string speak();
}
class Animal : IAnimal {
string speak() => "...";
}
class Dog : Animal {
string speak() => "Woof";
}
class Counter {
int n = 0;
void bump() { n = n + 1; }
int add(int k) => n + k;
}
class User {
string name;
new FromName(string s) { name = s; }
}
namespace Util {
int twice(int n) => n * 2;
}
void main() {
var twice = Util::twice;
console.writeln(twice(21));
(Counter, int) => int adder = Counter::add;
Counter c = Counter();
c.bump();
console.writeln(adder(c, 10));
var make = User::FromName;
console.writeln(make("Zed").name);
var bump = c.bump;
bump();
bump();
console.writeln(c.n);
(IAnimal) => string viaInterface = IAnimal::speak;
(Animal) => string viaClass = Animal::speak;
console.writeln(viaInterface(Dog()));
console.writeln(viaClass(Dog()));
Array<(int) => int> fs = [Util::twice, (n) => n + 1];
console.writeln(fs.at(0)(5));
}
main();
42
11
Zed
3
Woof
Woof
10
Rules
- Overloads are resolved by the target function type. When the referenced name is overloaded, the compiler picks the overload that matches the declared type of the variable, field or parameter that receives the reference. A reference to an overloaded name with no target type (
var h = Counter::label;) is a compile error: write the target function type. - Missing members are a compile error at the reference.
- Generic callables cannot be referenced without type arguments:
M::identity, whereidentity<R>is generic, is a compile error. Supply the types with::<...>as inidentity::<int>, which is the marker for a reference to a generic function. - Dispatch follows the runtime object. A reference behaves like the lambda
(C c, ...) => c.m(...), so both an interface reference (IAnimal::speak) and a class reference (Animal::speak) run the override of the object that is passed in, as the example shows with aDog. Seelang.method-dispatch. - A bound reference needs a simple receiver. The receiver must be a local variable, a parameter, or
this. Something likeo.inner.save,this.field.methodormake().methodis a compile error that tells you to bind the receiver to a local first. This keeps the capture a one-time snapshot instead of silently re-evaluating an expression on every call. - A bound reference captures the object, not the variable. Later changes to the object are visible through the reference; assigning a different object to the original variable does not retarget the reference.
A bound reference captures the object it was taken from
class Counter {
int n = 0;
void bump() { n = n + 1; }
int get() => n;
}
void main() {
Counter a = Counter();
var bumpA = a.bump;
var getA = a.get;
bumpA();
console.writeln(getA());
a = Counter();
bumpA();
console.writeln(a.n);
console.writeln(getA());
}
main();
1
0
2
Examples
class Counter {
string label(int k) => "int";
string label(string s) => "string";
}
namespace M { R identity<R>(R x) => x; }
class Inner { void save() { } }
class Outer { Inner inner = Inner(); }
void main() {
var a = Counter::label;
// error: ambiguous method reference 'Counter::label'; annotate the target function type
var b = M::identity;
// error: cannot reference generic function 'identity' in value position
Outer o = Outer();
var c = o.inner.save;
// error: bound method reference receiver must be a bare local, parameter, or 'this'
}
not run — shows a compile error
See also
- Array — An ordered sequence of values of one type,
Array<T>, with value semantics.