Manual contents

Generics

Generics let types and functions be reused with multiple static types.

Generic Types

type Box<T> {
  var value: T;

  func get(): T {
    return self.value;
  }
}

let number = Box<int> { value: 42 };
let text = Box<string> { value: "Feng" };

Type construction requires explicit type arguments. Feng does not infer a type declaration's type parameters from constructor arguments or context.

Generic Functions

func identity<T>(value: T): T {
  return value;
}

let first = identity(42);
let second = identity<string>("Feng");

At a call site, type arguments can be written explicitly or inferred when the arguments, receiver, or target type determine them uniquely.

Multiple Type Parameters

type Pair<T, U> {
  let first: T;
  let second: U;
}

func make_pair<T, U>(first: T, second: U): Pair<T, U> {
  return Pair<T, U> { first: first, second: second };
}

Generic Constraints

A constraint must refer to a spec:

spec Named {
  let name: string;
}

func name_of<T: Named>(value: T): string {
  return value.name;
}

An object-contract constraint makes the contract's members directly available in the generic implementation. A callable-contract constraint allows the parameter to be called directly. A union-contract constraint still requires narrowing with match first.

Generic Methods

type Box<T> {
  let value: T;

  func pair_with<U>(other: U): Pair<T, U> {
    return Pair<T, U> { first: self.value, second: other };
  }
}

A method's own type parameters cannot reuse the names of the enclosing type's parameters.

Invariance

Generic instances are invariant. Even if Dog satisfies Animal, Box<Dog> does not automatically convert to Box<Animal>. When such a conversion is needed, iterate explicitly and create a new target container or adapter object.

An unconstrained type parameter provides no members, comparison operations, or logical operations. A generic implementation can use only the basic operations available to every type and the capabilities supplied by its declared constraints.