DriftScript
Documentation

Guide

Values and mutability

let binds immutably, var does not, and mut sits on a parameter rather than on a type.

Bindings are immutable unless they say otherwise, and mutability is a property of a parameter rather than of a type. Both of those are decisions with consequences you can see from the first file you write.

let and var

`let` binds and cannot be reassigned. `var` can. Immutable is the default because it serves reasoning, hot reload, task safety and deterministic review at once, and because a compiler that can tell a mutation from a read is what makes a system's declared reads and writes checkable at all.

// Integers say what they do when a result does not fit. There is no undefined behaviour.
//
// A plain `+` fails rather than producing a value outside the type. `+%` wraps and `+|` saturates,
// and choosing is the point: a health bar saturates, a hash wraps, a count should fail.

fn health(current: u8, healed: u8) -> u8 {
    return current +| healed
}

fn hashStep(accumulator: u32, value: u32) -> u32 {
    return accumulator *% value
}

// Units are erased. `30m` is the number 30; `250ms` is 0.25, because seconds are the base unit.
fn reach() -> f32 {
    return 30m
}

fn beat() -> f32 {
    return 250ms
}

mut is on the parameter

A function says which of its arguments it may write. `door` below is declared `mut`, so its fields may be written; `by` is not, so it may only be read. Writing through a parameter that is not `mut` is an error naming the parameter.

// A record, a mutation, and the two things the compiler will not let you skip.
//
// `door` is `mut`, so its fields may be written. `by` is not, so it may only be read. A record
// literal gives every field a value: there is no partial construction.

import { clamp } from "std/math"

data Door {
    open: bool = false
    angle: f32 = 0
}

fn swing(door: mut Door, by: f32) {
    door.angle = math.clamp(door.angle + by, 0, 180deg)
    if door.angle > 45deg {
        door.open = true
    }
}

fn closed() -> Door {
    return Door { open: false, angle: 0 }
}

Why not a mut type

Were `Door` and `mut Door` different types, every signature accepting either would double, and a consumer's own type would fork the first time somebody needed to read one and write another. Putting it on the parameter keeps one type and moves the question to the place that answers it.