Types
Generics
Generic classes and functions, how type arguments are inferred or given explicitly with ::<T>, invariance, T::member and type constructors.
since 0.1.0-alpha.1linuxwindowswasm
Description
A class, a function or a method can take type parameters: class Box<T>, R identity<R>(R x), U map<U>(...). In a type, an instantiation is written Name<T1, ...>, as in Box<int>.
The compiler infers type arguments, in this order:
- from the types of the constructor or call arguments, including through containers (an
Array<U>parameter against anArray<int>argument givesU = int); - from the target type of the initializer, assignment or return that receives the result.
When inference is not enough, or you want to state the types, give them at the call with ::<...>: Box::<int>(), identity::<string>("x"), items.remap::<string>(fn).
Generic class and functions
class Box<T> {
T item;
T get() => this.item;
U map<U>((T) => U f) => f(this.item);
}
R identity<R>(R x) => x;
Box<int> a = Box(5);
console.writeln(a.get());
console.writeln(a.map((n) => "n=${n}"));
Box<string> b = Box::<string>("hi");
console.writeln(b.get());
console.writeln(identity(41) + 1);
console.writeln(identity::<string>("x"));
void demo() {
var f = identity::<int>;
console.writeln(f(7));
}
demo();
5
n=5
hi
42
x
7
Rules
Writing and inferring type arguments.
- A type position uses angle brackets:
Name<T1, T2>. Instantiations nest:Array<Array<int>>. - A call uses the turbofish,
callee::<T1, ...>(args).f<T>(x)written without::is a comparison, never a generic call. - The turbofish with no argument list,
identity::<int>, is a pinned reference to the generic function as a value. An unpinned reference,var f = identity;, is a compile error that suggests the turbofish. - An explicit list is all-or-nothing and exact. Its length must equal the generic arity of the constructor, function or method:
Box::<int, int>(1)and a one-argument list for a two-parameter function are errors. - An explicit list needs a declared function, method or constructor. It cannot be applied to a closure or another function value.
- Receiver class arguments are independent of a method's explicit list:
items.remap::<string>(fn)supplies only the method's parameter.
Variance.
- Generics are invariant: a
Box<int>is not aBox<float>, even thoughintconverts tofloat. - The raw form, the generic name without arguments (
Box), is compatible with any instantiation of the same generic.
T::member.
- Inside a generic function, the left side of
::may be one of that function's own type parameters:A::FromInt(n)calls the labeled constructorFromIntof whatever typeAis at the call. - The member is checked for each concrete type used. If the type lacks the member, the error names the type and points to both the
A::memberuse and the call that instantiated it. T::works only for type parameters of the function. A class-level parameter is rejected: "'T' is a class-level type parameter".
Type constructors.
- A type parameter may stand for a generic type that takes an argument, written
F<A>. Inference bindsFto the generic name:F<A>againstArray<int>givesF = ArrayandA = int, and the result typeF<B>keeps the container. - This is an advanced idiom. Prefer ordinary methods for everyday code.
Examples
T::member and a type constructor:
T::member and a type constructor
class Meters {
int n = 0;
new FromInt(int v) { this.n = v; }
}
class Wrapper<T> {
T item;
new Of(T v) { this.item = v; }
}
A decode<A>(A witness, int n) => A::FromInt(n);
F<B> mapIt<F, A, B>(F<A> c, (A) => B fn) => c.map(fn);
Meters m = decode(Meters(), 12);
console.writeln(m.n);
Wrapper<string> w = Wrapper::Of::<string>("lab");
console.writeln(w.item);
Array<int> d = mapIt([1, 2, 3], (n) => n * 2);
console.writeln(d);
12
lab
[2, 4, 6]
Notes
Not supported: using T::member where T is a class-level type parameter.
See also
- Type expressions — How a type is written: names, qualified names, generic instantiations, unions, optionals and function types.