Typing is strict: You cannot put a round peg in a square hole.

Since all values, expressions and variables have a type, the compiler can (and will) check whether the use of types is consistent. In most cases, an error will be reported wherever a program:

These constraints protect against the program manipulating a value in ways that make no sense or could be harmful, due to acting as if the value has a different meaning than it actually does.

However, sometimes we do need to transform a value of one type to another type. Cone supports three such capabilities:

Subtyping and Implicit Type Coercion

In many cases, one type can be a subtype of another type. A subtype specializes its more general-purpose supertype, adding additional constraints. Many forms of subtyping are supported:

Coercion maps a value to its exact equivalent in a supertype. Cone will automatically and implicitly coerce a value of some subtype to its supertype when a supertype value is expected. Commonly, this happens with assignments and function calls:

// Implicitly coerce a u8 to u32
imm u8value = 101u8
imm bigint u32 = u8value

// Implicitly coerce &own uni Mat4 to &const Mat4
mut matrix = &own Mat4[]
imm det = mat4det(matrix)
fn mat4det(mat &Mat4) f32
   // ...

Coercion can be requested explicitly by using the 'into' operator:

imm bigint = u8value into u32

Type Conversion

Type conversion involves transforming a value of one type to its "equivalent" value in another type. For example, converting the integer 1 to the floating point number 1.0, or serializing it to text: "1". Unlike coercion, conversion may not map perfectly: A valid mapping may not exist or some precision may be lost.

Converting one number type to another uses the "literal" format or the 'into' operator:

imm float = f32[1]
imm float = 1 into f32

Beyond number types, conversion is typically requested using the new type's initializer or a method in the old type:

imm text = Text(1) // "1"
imm text = 1.text

Type Reinterpretation

Unlike coercion and conversion, reinterpretation does not create a new value. It interprets the existing encoded value as if it has a different type. The new type must have the same size as the current type of the value.

Reinterpretation uses the as infix operator. Unlike other infix operators, as expects a type (rather than a value or expression) to the right of the operator.

Reinterpretation is mostly used on references, such as when turning a reference into a pointer.

	imm ptr = &x as *Vector3

Reinterpretation can be also be used on number types, such as when we want to access a floating point number's bits:

	imm highword = u32[(pi as u64) >> 32]

Reinterpretation can be dangerous, as a value could behave badly when treated and manipulated within the constraints of a different type. For example, a reference reinterpreted as a pointer loses permission constraints, making it possible for a pointer to be used to alter an immutable object. It is the responsibility of the programmer to ensure the reinterpreted value is handled in a safe and useful manner.