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.
Generic functions
Section titled “Generic functions”Type parameters appear in square brackets after the function name:
func identity[T](value : T) -> T do valueend
func main() -> Unit do number = identity(42); text = identity("Noodle"); Debug.trace(number); Console.println(text);endEach call instantiates T independently from its arguments and expected
result. Noodle does not globally generalize arbitrary local bindings; reusable
polymorphism is stated on declarations.
Relate several types
Section titled “Relate several types”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));endapply does not know the concrete types. It only promises that transform
accepts the same A as value and determines the returned B.
Generic datatypes and aliases
Section titled “Generic datatypes and aliases”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._0end
func main() -> Unit do boxed : Box[String] = Box::Box("Noodle"); Console.println(unbox(boxed));endConstructor 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.
Inference stays local
Section titled “Inference stays local”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.
Require operations through capabilities
Section titled “Require operations through capabilities”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.