Data Types

Kōdo has a rich type system with primitive types, composite types (struct, enum), generics, and collections. There is no null — use Option<T> instead. There are no exceptions — use Result<T, E> instead.

Primitive Types

Every value in Kōdo has a known type at compile time. The built-in primitive types are:

TypeDescriptionExample
Int64-bit signed integer (default)42, -1, 0
Int88-bit signed integer127
Int1616-bit signed integer32000
Int3232-bit signed integer2_000_000
Int6464-bit signed integer (same as Int)9_000_000_000
Uint64-bit unsigned integer0
Uint88-bit unsigned integer255
Uint1616-bit unsigned integer65535
Uint3232-bit unsigned integer4_294_967_295
Uint6464-bit unsigned integer0
Float3232-bit floating point3.14
Float6464-bit floating point (default float)2.718281828
BoolBooleantrue, false
StringUTF-8 string"hello"
ByteSingle byte (8-bit unsigned)0
UnitNo value (like void)implicit

Integers

Int is the default integer type — 64-bit signed. Use the sized variants when you need explicit control over memory layout:

let count: Int = 42
let small: Int8 = 100
let big: Uint64 = 18_446_744_073_709_551_615

All integer types support arithmetic (+, -, *, /, %), comparison (==, !=, <, >, <=, >=), and bitwise operations. Integer overflow wraps silently (two’s complement).

Booleans

let active: Bool = true
let done: Bool = false

if active {
    println("still running")
}

Booleans support && (and), || (or), and ! (not).

Strings

Strings are UTF-8, heap-allocated, and reference-counted. They support escape sequences (\", \\, \n, \t, \r, \0) and f-string interpolation:

let name: String = "Kōdo"
let greeting: String = f"Hello, {name}!"
let escaped: String = "line1\nline2"

See String Interpolation for f-strings and Standard Library for string methods (length, contains, split, replace, trim, etc.).

:::note[String Caveat] The substring method operates on byte offsets, not Unicode character boundaries. For ASCII strings this is identical, but for strings containing multi-byte characters (e.g., "Kōdo"), byte offsets may not align with character positions. Unicode-aware string indexing is planned for a future release. :::

No Null, No Exceptions

Kōdo has no null and no exceptions. Instead:

  • Optional values use Option<T> — either Option::Some(value) or Option::None
  • Fallible operations use Result<T, E> — either Result::Ok(value) or Result::Err(error)
let found: Option<Int> = Option::Some(42)
let missing: Option<Int> = Option::None

let ok: Result<String, String> = Result::Ok("success")
let err: Result<String, String> = Result::Err("not found")

See Error Handling for details on Option and Result.

Structs

A struct groups related values under named fields:

struct Point {
    x: Int,
    y: Int
}

Creating Structs

Create a struct value by providing all fields:

let origin: Point = Point { x: 0, y: 0 }
let p: Point = Point { x: 3, y: 4 }

Accessing Fields

Use dot notation to access individual fields:

fn get_x(p: Point) -> Int {
    return p.x
}

fn main() {
    let p: Point = Point { x: 10, y: 20 }
    print_int(p.x)
    print_int(p.y)
}

Structs as Function Parameters

Structs can be passed to and returned from functions:

struct Point {
    x: Int,
    y: Int
}

fn translate(p: Point, dx: Int, dy: Int) -> Point {
    return Point { x: p.x + dx, y: p.y + dy }
}

fn main() {
    let p: Point = Point { x: 1, y: 2 }
    let q: Point = translate(p, 10, 20)
    print_int(q.x)
    print_int(q.y)
}

Output:

11
22

Internally, structs are passed by pointer — there is no copy overhead for large structs.

Enums

An enum defines a type that can be one of several named variants. Each variant can optionally carry data:

enum Color {
    Red,
    Green,
    Blue
}

enum Shape {
    Circle(Int),
    Rectangle(Int, Int)
}

Color has three variants with no data. Shape has two variants: Circle carries a radius (Int) and Rectangle carries width and height (Int, Int).

Creating Enum Values

Use the EnumName::Variant(args) syntax:

let s: Shape = Shape::Circle(5)
let r: Shape = Shape::Rectangle(10, 20)

Pattern Matching with match

match is the primary way to work with enums. It destructures the value and binds contained data to variables:

fn area(s: Shape) -> Int {
    match s {
        Shape::Circle(r) => {
            return r * r * 3
        }
        Shape::Rectangle(w, h) => {
            return w * h
        }
    }
}

Each arm of a match specifies a pattern (Shape::Circle(r)) and a body to execute when the value matches that pattern. The contained values are bound to the names you choose (r, w, h).

Match is Exhaustive

Every match must cover all variants. If you forget one, the compiler will tell you. This prevents a common category of bugs where a new variant is added but not handled everywhere.

Enums as Function Parameters

Like structs, enums can be passed to and returned from functions:

enum Direction {
    Up,
    Down,
    Left,
    Right
}

fn move_point(p: Point, dir: Direction, amount: Int) -> Point {
    match dir {
        Direction::Up => {
            return Point { x: p.x, y: p.y + amount }
        }
        Direction::Down => {
            return Point { x: p.x, y: p.y - amount }
        }
        Direction::Left => {
            return Point { x: p.x - amount, y: p.y }
        }
        Direction::Right => {
            return Point { x: p.x + amount, y: p.y }
        }
    }
}

Complete Example

Here’s a program that combines structs, enums, and pattern matching:

module geometry {
    meta {
        purpose: "Calculate areas of shapes"
        version: "0.1.0"
    }

    struct Point {
        x: Int,
        y: Int
    }

    enum Shape {
        Circle(Int),
        Square(Int),
        Rectangle(Int, Int)
    }

    fn area(s: Shape) -> Int {
        match s {
            Shape::Circle(r) => {
                return r * r * 3
            }
            Shape::Square(side) => {
                return side * side
            }
            Shape::Rectangle(w, h) => {
                return w * h
            }
        }
    }

    fn main() {
        let c: Shape = Shape::Circle(5)
        let sq: Shape = Shape::Square(4)
        let r: Shape = Shape::Rectangle(3, 7)

        print_int(area(c))
        print_int(area(sq))
        print_int(area(r))
    }
}

Output:

75
16
21

Float64

Kōdo supports 64-bit floating-point numbers with full arithmetic:

let pi: Float64 = 3.14159
let radius: Float64 = 5.0
let area: Float64 = pi * radius * radius

println_float(area)

All arithmetic (+, -, *, /, %), comparison (==, !=, <, >, <=, >=), and negation (-x) operators work with Float64 values.

String Operators

Strings support concatenation with the + operator:

let greeting: String = "Hello, " + "world!"
println(greeting)

let name: String = "Kōdo"
let msg: String = "Welcome to " + name
println(msg)

Strings also support equality comparison with == and !=, which compares by content (not by pointer):

let a: String = "hello"
let b: String = "hello"
if a == b {
    println("strings are equal")
}

See Standard Library for the full list of string methods (length, contains, split, trim, concat, index_of, replace, etc.).

String Methods

split(separator)

Splits a string by a separator, returning a List<String>:

let parts: List<String> = "a,b,c".split(",")

parse_int()

Parses a string as an integer. Returns 0 on failure:

let valid: Int = "42".parse_int()     // 42
let bad: Int = "hello".parse_int()    // 0

Collections

Kōdo provides built-in collection types available in every program without an import.

List<T>

A dynamic array of elements, accessed via free functions:

let nums: List<Int> = list_new()
list_push(nums, 10)
list_push(nums, 20)
list_push(nums, 30)
let len: Int = list_length(nums)         // 3
let first: Int = list_get(nums, 0)       // 10
let has: Bool = list_contains(nums, 10)  // true

Full List API

FunctionDescription
list_new()Create a new empty list
list_push(list, value)Append a value to the end
list_get(list, index)Get value at index
list_length(list)Number of elements
list_contains(list, value)Check if value exists
list_pop(list)Remove and return the last element
list_remove(list, index)Remove element at index
list_set(list, index, value)Set value at index
list_slice(list, start, end)Get a sub-list from start to end (exclusive)
list_sort(list)Sort the list in ascending order (in place)
list_join(list, separator)Join list elements into a String with separator

Map<K, V>

A generic key-value hash map. The type is determined by the annotation on the let binding:

// Map<Int, Int> — fully supported
let scores: Map<Int, Int> = map_new()
map_insert(scores, 1, 100)
let val: Int = map_get(scores, 1)           // 100

:::caution[Map Type Support] The following Map<K, V> type combinations are fully supported end-to-end (type checking + codegen + linking):

KVStatus
IntIntFully supported
StringStringFully supported
IntStringType checks, but may produce link errors
StringIntType checks, but may produce link errors

If you need a combination that is not fully supported, use Map<Int, Int> or Map<String, String> and convert keys/values as needed. Full monomorphization for all type combinations is being implemented. :::

Map API

All functions work with any Map<K, V> where K, V are Int or String:

FunctionDescription
map_new()Create a new empty map (type from annotation)
map_insert(m, k, v)Insert or update a key-value pair
map_get(m, k)Get value by key
map_contains_key(m, k)Check if key exists
map_length(m)Number of entries
map_remove(m, k)Remove a key-value pair
map_is_empty(m)Check if map is empty

Next Steps

  • Closures — closures, lambda lifting, and higher-order functions
  • Generics — parameterize your types with type variables
  • Error Handling — use Option<T> and Result<T, E> for safe error handling
  • Contracts — add runtime verification to your functions