Type variables

In N code, you'll often encounter mysterious letters like list[t] in certain types. For example, the print function:

assert type print : [t] t -> t

That [t] in the function type annotation means that you can substitute t with any type. This allows print to be able to accept and return any type, not just strings:

assert type print("Hello!") : str
assert type print(3.14) : float

The same t is used in the function's argument and return type, which means that the return type must be the same as the argument type.

For a more sophisticated example, let us consider map, whose much more complicated type signature is shown below. However, when broken down, it is much simpler than it looks.

assert type map : [a, b] (list[a], (a -> b)) -> list[b]

map is a function that takes another function (the transformer) and a list, then returns a new list containing items of a different type, each transformed by the magic transformer function.

Here, [a, b] declares two type variables. This means that the argument of the transformer function a -> b and the items of the list list[a] must be the same type, but the items of the list list[a] do not have to be the same type as the return type (b) of the transformer function because a and b can stand for different types.

For example, intInBase10 converts ints to strs (int -> str), so we know that a must stand for an int, and b must stand for a str. Since a must stand for the same type throughout the function, and likewise for b, we can determine the type of map(intInBase10).

assert type map(intInBase10) : list[int] -> list[str]

At the time of writing this, map is not a built-in function provided by N. And the intInBase10 function has been moved to the trait of int, toString

Type variables can also be used in enum and alias declarations. For example, you could make an alias for a tuple of three of the same type:

alias triplet[t] = (t, t, t)

N's result type can be defined in N using type variables as well:

type result[o, e]
	| ok(o)
	| err(e)

In the above two examples, the [t] is needed to declare the type variable t. Otherwise, as warned above, N will give an error about how type t is undefined.

Since the types take type variables, you need to specify them explicitly, in the same order as they are defined, to substitute the type variables in their type definitions. For example,

let vector3: triplet[float] = (0.5, -0.5, 3.14)

let myResult: result[int, str] = err("Failure!")