Note: Most of this is implemented to some degree.

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 a 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 want to transform a value of one type to another type. Cone supports three such capabilities:

To distinguish between coercion and conversion, it is first necessary to understand how subtyping works in Cone.

Subtypes

Subtyping captures a simple but powerful idea: if we have two types named Base and Subtype, how do we determine when it is safe to substitute in a value of type Subtype whenever our program logic is looking for a value of type Base?

Here are two essential criteria (there are others):

Many type pairs in Cone comply with these criteria and support a safe subtyping relationship:

An important characteristic of subtyping relationships is that it is trivially safe to transform a Subtype value into its equivalent Base value. However, the journey of a value back from Base to Subtype often requires some sort of pattern-matched guard, as there are likely to be values in Base that do not have a valid mapping from Subtype to Base. A successful pattern match guarantees we will get back a valid Subtype value when the reverse transformation is applied.

Implicit Coercion

We call it coercion when implicitly converting a value into an expected type (usually a supertype). Coercion is the one safe exception to the strict typing rule described earlier, given that the type of a value and its receiver are not strictly the same.

Since coercion is always safe, Cone performs it implicitly when copying or moving a subtype value to any container (variable, parameter, return value, field, reference) expecting a base value. This happens during assignments, function calls and returns. For example:

fn doStuff ():
  imm bigint u32 = 101u8        // Coerce a u8 to u32
  mut matrix = &so Mat4[]       // Allocate a matrix
  imm float = mat4det(matrix)?  // Coerce &so uni Mat4 to ?&Mat4

fn mat4det(mat &Mat4) f32 throws DivByZero:
  // ...
  return 1.0                     // Coerce f32 to Result<>

Coercion is more than just changing the type of some value. It usually means creating a new value whose encoding is different than its source value.

Coercion can also trigger compiler mechanics: In the case of coercing an owning reference to a borrowed reference, it triggers appropriate borrowing mechanics on the source variable. Similarly, when coercing a non-copy value, move semantics apply.

If an expression requires, it is possible to explicitly request coercion. For non-reference types, use the appropriate constructor (as described below for conversion). For references, use the as operator:

imm newref = oldref as &Point  // Explicitly coerce to a borrowed reference

Implicit Coercion to Bool

In addition to supertype coercions, Cone supports the automatic coercion of some value to Bool, so long as the value's type implements the isTrue method. This is implemented by the number and pointer types. A number is true if it is not zero. A pointer is true if it is not null.

Implicit Coercion of 'self'

Cone offers a very different form of coercion when dealing with method calls (or field access) and the 'self' value. This convenience sugar automatically coerces self from a value to a reference (or the other way).

Cone will automatically transform 'self' to a mutable borrowed reference for operators that expect an lval, such as ++, +=, or a 'set' method.

++a    // equivalent to:  Type::`++`(&mut a)

Automatic de-reference of 'self' may happen when 'self' is a reference, but no method matches with a reference-based self. 'self' is then automatically dereferenced in hopes of finding a matching method that uses a value rather than a reference.

&a + 5  // may coerce to:  a + 5

Explicit Conversion

Type conversion involves transforming a value of one type to the closest possible 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.

Conversion is typically requested using the new type's constructor or invoking one of its initializers

imm float = f32[1]
imm text = Text(1) // "1"

In some cases, it may be more convenient to request conversion by using some method in the old type:

imm text = 1.text

_