Generics
Teal supports a simple form of generics that is useful enough for dealing collections and algorithms that operate over abstract data types.
You can use type variables wherever a type is used, and you can declare them in both functions and records. Here's an example of a generic function:
local function keys<K,V>(xs: {K:V}):{K}
local ks = {}
for k, v in pairs(xs) do
table.insert(ks, k)
end
return ks
end
local s = keys({ a = 1, b = 2 }) -- s is {string}
we declare the type variables in angle brackets and use them as types. Generic records are declared and used like this:
local type Tree = record<X>
{Tree<X>}
item: X
end
local t: Tree<number> = {
item = 1,
{ item = 2 },
{ item = 3, { item = 4 } },
}
A type variable can be constrained by an interface, using is
:
local function largest_shape<S is Shape>(shapes: {S}): S
local max = 0
local largest: S
for _, s in ipairs(shapes) do
if s.area >= max then
max = s.area
largest = s
end
end
return largest
end
The benefit of doing this instead of largest_shape(shapes: {Shape}): Shape
is that, if you call this function passing, say, an array {Circle}
(assuming that record Circle is Shape
, Teal will infer S
to Circle
,
and that will be the type of the return value, while still allowing you
to use the specifics of the Shape
interface within the implementation of
largest_shape
.
Keep in mind though, the type variables are inferred upon their first match, so, especially when using constraints, that might demand additional care.