DriftScript
Documentation

Guide

Options

There is no null. Absence is a type, and the compiler will not let you forget it.

There is no null in this language and no undefined. An option is the only way to say a value might not be there, and the compiler will not let you forget which you are holding.

some and none

T? is an option. A bare value does not flow into an option position: returning 1 where f32? was declared is an error, and some(1) is what was meant.

// An option is how a value says it might not be there. There is no null.

fn firstOrNone(present: bool) -> f32? {
    if present {
        return some(1)
    } else {
        return none
    }
}

// `if let` is how an option is read. There is no way to reach the value without answering the
// question first, which is the whole point.
fn readOr(value: f32?, fallback: f32) -> f32 {
    if let found = value {
        return found
    } else {
        return fallback
    }
}

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

fn find(present: bool) -> Door? {
    if present {
        return some(Door { open: true, angle: 90deg })
    } else {
        return none
    }
}

// `?.` reaches through an option and the result is still an option, which is deliberate: an
// operation that may not have run must not be treated as though it did.
fn angleOf(present: bool) -> f32? {
    let door = find(present)
    return door?.angle
}

Why the bare value is refused

Accepting it would reinvent implicit null in the other direction. A reader could no longer tell an absence that was decided from one that was never filled in, which is the whole thing an option exists to distinguish.

// Deliberately refused.
//
// A bare value does not flow into an option position. `return 1` from a function returning `f32?`
// is an error, and `return some(1)` is what was meant. A language that accepted the first has
// reinvented implicit null in the other direction: a reader can no longer tell an absence that
// was decided from one that was never filled in.

fn find(present: bool) -> f32? {
    if present {
        return 1
    } else {
        return none
    }
}

Reaching through one

`?.` reaches through an option and the result is still an option. That is deliberate: an operation that may not have run must not be treated as though it did.

// An option is how a value says it might not be there. There is no null.

fn firstOrNone(present: bool) -> f32? {
    if present {
        return some(1)
    } else {
        return none
    }
}

// `if let` is how an option is read. There is no way to reach the value without answering the
// question first, which is the whole point.
fn readOr(value: f32?, fallback: f32) -> f32 {
    if let found = value {
        return found
    } else {
        return fallback
    }
}

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

fn find(present: bool) -> Door? {
    if present {
        return some(Door { open: true, angle: 90deg })
    } else {
        return none
    }
}

// `?.` reaches through an option and the result is still an option, which is deliberate: an
// operation that may not have run must not be treated as though it did.
fn angleOf(present: bool) -> f32? {
    let door = find(present)
    return door?.angle
}
An absent answer prints as none, because that is what the language calls it.