Slide 34 of 74

Generics

A generic callable
func first(v: T[]): Result<T, Bool> {
	if len(v) > 0 {
		return Result.Ok(v[0])
	}
	return Result.Error(false)
}

proc main(): void {
	a := first([1, 2, 3])       // first_Int
	b := first(["x", "y"])      // first_Str, a separate copy
	echo "{a} {b}"
}
A generic type
type Box {
	items: T[dyn]
	label: Str
}

type Either {
	Left  { left: A }
	Right { right: B }
}

proc main(): void {
	Box<Str> people = Box(["ada", "grace"], "people")
	Box<Int> counts = Box([1, 2, 3], "counts")
	Either<Str, Int> answer = Either.Right(42)

	echo "{people.label}: {people.items}"
	echo "{counts.label}: {counts.items}"
	if answer is Either.Right(n) { echo n }
}

A name in a signature that is neither a builtin type nor a declared one is a type variable, and it makes the callable generic. A variable is pinned down by where it appears among the parameters — including inside a parameter's own function type. Nothing about it is dynamic: every call is resolved when the program compiles, and one concrete copy is written per distinct set of type arguments — first_Str, first_Int — with no boxing and no dispatch. A type may take variables the same way, and is written with them filled in: Box<Str>, Either<Str, Int>.

Worth knowing:

  • A misspelt type name becomes a type variable rather than an error: func greet(u: Usr): Str is generic, and accepts anything. Check the spelling when a function takes more than it should.
  • A variable has to appear in a parameter, because that is where a call pins it down; one that appears only in the return type is refused.
  • A generic body is checked once per copy, when it is called, so a mistake in one nobody calls goes unreported, and one in a called copy is reported under that copy's name, like broken_Int.
  • A generic union's variant is built where a declaration says which type it is — Either<Str, Int> answer = Either.Right(42) — since the variant alone does not say what the other side holds.
  • A generic callable is not a value: which copy a reference means depends on argument types it does not have.