Types in Tenet

Type Expressions

Type expressions appear in type definitions, function signatures, and annotations. In general, Tenet tries to make types homoiconic, that is, types usually look like the values they represent.

Value Types

In Tenet, a value is defined by some properties:

The practical implication of Tenet’s values is that any value can be placed in any variable, or stored within a record or collection. They can always be consistently serialized and deserialized.

Atomic Types

The current types are fairly basic; we’ll add Float and put some bounds on Int.

Algebraic Types

Records

A record is a fixed collection of named fields with potentially different types. Record types are closed — two records are compatible only if they have exactly the same field names and compatible field types.

type Point = (x: Int, y: Int)
let p = (x: 3, y: 4)

Tagged Unions

A union is a discriminated sum type. Every member is a tagged value tag ~ variant and an expression #tag is shorthand for tag ~ (), meaning the variant is the unit record value.

type Result =
    ok ~ Str
  | #err

type Optional = just ~ Int | #nothing

Unions are order-independent within Tenet semantics.

Container Types

Lists have a type \[T], or List\[T] in full. They are a homogeneous finite sequence of values of type T. A list value literal is written as \[a, b, c, d], and the empty list is \[].

**Sets** have a type `Set\[T]`. They are a homogeneous finite set of values of type `T`. A set value literal is written as `set\[a, b, c, d]`, and the empty set is `set\[]`.

Maps have a type \[K: V], or Map\[K: V] in full. They are a finite mapping from keys of type K to values of type V. A map literal is written as \[a: b, c: d], and the empty map is \[:].

type Color = #red | #blue
let numbers : \[Int]     = \[1, 2, 3]
let colors : Set\[Color] = set\[#red, #blue]
let scores : \[Str: Int] = \["alice": 95, "bob": 87]

What’s missing

Some future features (objects, capabilities, stators, blocks) are missing, but we’re resolved to discuss them in a roadmap.

There’s no null

Tenet has no null or undefined special values. It’s not that we don’t like null, most languages have worked out how to tame it by making it plainly apparent.

The problem with null is it doesn’t go far enough: there’s only one null and we don’t know what it means. In Tenet, the user can plainly state that data is #missing, #corrupted, #blank, #lost, #blithering or whatever meaningful label applies.

Function types

Again, function types and functions as values are great. They raise some thorny questions:

We hold that a function fundamentally isn’t a value. Tenet will offer blocks that can be passed to functions and act as though they are inlined, and functors that are callable objects.

Special Types

These aren’t missing, but they’re not immediately user accessible.

Bottom ()

The empty type, used internally during type inference.

Top ()

This is used internally. In inference, we often need to find a common supertype of multiple types, and if this isn’t available, Top is a marker to indicate this.