Skip to content
Noodle
InstallLearnPlayground
GitHub

Generic programming

Generics are useful when an operation should work uniformly for many types without discarding static information. Noodle uses explicit declaration-site type parameters and local call-site inference.

Type parameters appear in square brackets after the function name:

func identity[T](value : T) -> T do
value
end
func main() -> Unit do
number = identity(42);
text = identity("Noodle");
Debug.trace(number);
Console.println(text);
end

Each call instantiates T independently from its arguments and expected result. Noodle does not globally generalize arbitrary local bindings; reusable polymorphism is stated on declarations.

Multiple type parameters express how inputs and outputs correspond:

func apply[A, B](transform : (A) -> B, value : A) -> B do
transform(value)
end
func to_label(value : Int) -> String do
"item-" ++ value.to_string()
end
func main() -> Unit do
Console.println(apply(to_label, 7));
end

apply does not know the concrete types. It only promises that transform accepts the same A as value and determines the returned B.

Datatypes and type aliases may also declare type parameters:

datatype Box[T]
Box(T)
end
type Pair[T] = (T, T)
func unbox[T](box : Box[T]) -> T do
box._0
end
func main() -> Unit do
boxed : Box[String] = Box::Box("Noodle");
Console.println(unbox(boxed));
end

Constructor type arguments are normally inferred from payloads or an expected datatype. A payload-free generic constructor needs an expected type or an explicit datatype application, such as Option[Int]::None.

Function parameters and results remain annotated even when they mention type parameters. Recursive calls use the current declaration’s rigid type parameters; they do not silently choose a new instantiation inside the same generic body.

An empty or otherwise unconstrained generic value needs nearby type information. For standard-library arrays, for example, [] requires an expected Array[T]; the Arrays chapter covers that case.

An unconstrained T provides no type-specific operations. Generic equality, ordering, arithmetic, iteration, and similar behavior is requested through a contextual interface extension rather than assumed from the type parameter.

That syntax builds on question parameters, so continue with Question parameters and then Interfaces and extensions. The Structural capabilities chapter then shows how generic equality, ordering, hashing, and debugging use those mechanisms.