Templates

Declaring templates

To write a function that works for many different types, you would use a template.
To make a declaration a template, write [] after the name, with type parameter names listed inside, as in any[t].
(The [] have a different meaning on a declaration than they do for a map type v[k]).

main void() nats nat[] = 1, 2, 3 strings string[] = "one", "two", "three" info log "{nats any x => x is-even}" info log "{strings any x => x == "pi"}" # This is called 'exists' in the the standard library any[t] bool(a t[], cb fun bool(t)) for x : a if cb[x] break true else false

any is technically a function template and not a function. It is instantiated into two different functions.
The first (nats any x => x is-even) calls an instantiation of any where t is replaced by nat.
The second call (strings any x => x == "pi") uses string instead.

Explicit type arguments

In the above example, Crow infers how to instantiate any based on the first argument. (It gets a nat[] and needs a t[], so t must be nat.)
You should almost always rely on inference. But when you do get a compile error, you can often get a better error by specifying what the type arguments should be.
The syntax for this looks like any@nat. When the template takes multiple types or the type argument is a complex type, write it with parentheses, like combinations@(nat, string).

main void() nats nat[] = 1, 2, 3 strings string[] = "one", "two", "three" info log "{nats any@nat x => x is-even}" for x, y : nats combinations@(nat, string) strings info log "{x} {y}" # This is like 'exists' from the standard library any[t] bool(a t[], cb fun bool(t)) for x : a if cb[x] break true else false combinations[t, u] (t, u)[](a t[], b u[]) with out : build for x : a for y : b out ~= (x, y)

Templated types

Just like there are function templates, there are type templates.

main void() a nat by-name = (("one", 1), ("two", 2)), 99 b string by-name = (("one", "uno"), ("two", "dos")), "varias" info log "{a["one"]}" info log "{a["three"]}" info log b["three"] by-name[t] record(values t[symbol], default t) subscript[t] t(a t by-name, name symbol) a.values[name] ?? a.default

The syntax to instantiate a type template looks like nat by-name, where nat is the type argument and by-name is the template.
If there were multiple type arguments, this would look like (nat, bool) by-name.

Type templates can be chained without parentheses. Each suffix applies from left to right. nat?[] is a list of optional nats, while nat[]? is an optional list of nats.

The purity of type arguments affects the purity of the type. So even though list is data, string-builder[] is mut because string-builder is.

Templates are safe

Templated functions are type-checked as templates, meaning before they are instantiated.
That means that expressions in a template must work for all possible types.

This avoids confusing compile errors where a template only fails to compile in certain instantiations. But, it does require additional work to write templates that rely on certain operations existing.

main void() info log "{()::nat[].sum}" # This will *not* compile sum[t] t(a t[]) res mut t = () for x : a res +:= x res

We'll see how to make this work in Specs.