In Teal we can declare new types with user-defined names. These are called nominal types. These nominal types may be unique, or aliases.
The local type
syntax produces a new nominal type. Whenever you assign to
it another user-defined nominal type, it becomes a type alias. Whenever you
assign to it a type constructor, it becomes a new unique type. Type
constructors are syntax constructs such as: block constructors for records,
interfaces and enums (e.g. record
... end
); function signature
declarations with function()
; applications of generics with <>
-notation;
declarations of array, tuple or map types with {}
-notation; or a primitive
type name such as number
.
Syntax such as local record R
is a shorthand to local type R = record
, so
the same rules apply: it declares a new unique type.
Nominal types are compared against each other by name, but type aliases are considered to be equivalent.
local record Point3D
x: number
y: number
z: number
end
local record Vector3D
x: number
y: number
z: number
end
local p: Point3D = { x = 1.0, y = 0.3, z = 2.5 }
local v: Vector3D = p -- Teal compile error: Point3D is not a Vector3D
local type P3D = Point3D
local p2: P3D
p2 = p -- ok! P3D is a type alias type Point3D
p = p2 -- ok! aliasing works both ways: they are effectively the same type
Nominal types are compared against non-nominal types by structure, so that you can manipulate concrete values, which have inferred types. For example, you can assign a plain function to a nominal function type, as long as the signatures are compatible, and you can assign a number literal to a nominal number type.
local type MyFunction = function(number): string
-- f has a nominal type
local f: MyFunction
-- g is inferred a structural type: function(number): string
local g = function(n: number): string
return tostring(n)
end
f = g -- ok! structural matched against nominal
g = f -- ok! nominal matched against structural
You can declare structural types for functions explicitly:
local type MyFunction = function(number): string
-- f has a nominal type
local f: MyFunction
-- h was explicitly given a structural function type
local h: function(n: number): string
f = h -- ok!
h = f -- ok!
By design, there is no syntax in Teal for declaring structural record types.
Type aliasing only happens when declaring a new user-defined nominal type using an existing user-defined nominal type.
local type Record1 = record
x: integer
y: integer
end
local type Record2 = Record1
local r1: Record1
assert(r1 is Record2) -- ok!
This does not apply to primitive types. Declaring a type name with the same primitive type as a previous declaration is not an aliasing operation. This allows you to create types based on primitive types which are distinct from each other.
local type Temperature = number
local type TemperatureAlias = Temperature
local type Width = number
local temp: Temperature
assert(temp is TemperatureAlias) -- ok!
assert(temp is Width) -- Teal compile error: temp (of type Temperature) can never be a Width
Like records, each declaration of a function type in the program source code
represents a distinct type. The function(...):...
syntax for type
declaration is a type constructor.
local type Function1 = function(number): string
local type Function2 = function(number): string
local f1: Function1
assert(f1 is Function2) -- Teal compile error: f1 (of type Function2) can never be a Function1
However, user-defined nominal names referencing those function types can be aliased.
local type Function1 = function(number): string
local type Function3 = Function1
local f1: Function1
assert(f1 is Function3) -- ok!