8 Type System


8. Type System

8.1 Types vs Traits

The language distinguishes between types (what things are) and traits (how things behave):

Types define the concrete structure and memory layout:

Traits define behavioral contracts:

Key Distinction:

Examples:

// Point is a type
(T T --) { x: y: } ::Point<T> struct

// Addable is a trait
{
    (Self Self -- Self) +:
    (Self Self -- Self) -:
} ::Addable trait

// Point implements Addable (Point now behaves additively)
::Addable {
    (Self Self -- Self) {
        over ::x get over ::x get +
        swap ::y get swap ::y get +
        Point
    } +:
    // ... subtract implementation ...
} ::Point impl

Related: See Section 9 for the complete trait system and Appendix B for all standard trait definitions. See Appendix H for detailed information about all types.

8.2 Type Inference

The compiler infers types in most situations, minimizing the need for explicit type annotations.

When Type Inference Works:

42              // Inferred as i64 (default integer)
3.14            // Inferred as f64 (default float)
[1 2 3]         // Inferred as array of i64

// Function return type inferred from body
(Number -- Number) { dup * } ::square fn
5 square        // Compiler knows result is Number

When Annotations Are Needed:

42:i32          // Explicit annotation for 32-bit integer
3.14:f32        // Explicit annotation for 32-bit float

// Ambiguous generic contexts may need hints
parse           // May need type context to know what to parse to

Type Inference with Generics:

// Type parameter T is inferred from usage
(T -- T) { } ::identity fn

5 identity      // T inferred as i64
"hello" identity // T inferred as String

Related: See Section 10 for generic programming and type parameter inference.

8.3 Type Tuples

Type tuples represent stack effects and are used in function signatures to specify what a function consumes from and produces to the stack.

Syntax: (inputs -- outputs)

Examples:

(T T -- T)               // Two inputs of type T, one output of type T
(i32 f64 -- String)      // Takes i32 and f64, returns String
(-- bool)                // No inputs, returns bool
(Point --)               // Takes Point, no outputs
(---)                    // No inputs, no outputs (side effects only)

Variable Arguments with Iterable:

For operations that need variable numbers of arguments, use the Iterable trait:

(-- Size) depth:                           // Zero arguments, returns stack depth
(String Iterable<Stringifiable> --) format: // String plus iterable of printable values
(Iterable<T> -- T) sum:                    // Iterable of values, returns sum

Examples:

"x=%d, y=%d" [x y] format    // Format string with array of values
[1 2 3 4 5] sum              // Sum array of numbers
depth print                   // No arguments needed

This approach keeps the type system simple without special variadic syntax.

8.4 Type Composition

Types can contain other types, creating composite data structures:

Nested Structures:

// Point contains two T values
(T T --) { x: y: } ::Point<T> struct

// Line contains two Points
(Point<T> Point<T> --) { start: end: } ::Line<T> struct

Arrays of Types:

[1 2 3]                          // Array of i64
[[1 2] [3 4]]                    // Array of arrays
[Point Point Point]              // Array of Points

Generic Composition:

// Box contains a value of any type
(T --) { value: } ::Box<T> struct

// Option can contain any type (or nothing)
(T --) { Some(T) None } ::Option<T> union

// Nested generics
Option<Point<f64>>               // Option containing a Point of f64s