Reference

std/core/option

std/core/src/option.trb

Absence as a value: Option, its two cases, and the vocabulary that works on the value that is there.

Value? is the only absence in the language - there is no null and no uninitialized field - so a signature says whether a value can be missing, and the caller has to answer for it. The answers are ?? for a fallback, ?. for a step that keeps the absence, and match for both ways at once.

Related

type Option

type Option<Value> with OrElse<Value>

A value that may be absent. Value? is sugar for Option<Value>.

Reach for it where nothing is a legitimate answer: a lookup that finds no entry, a list that has no first element, a field that is filled in later. map, flatMap, filter and forEach mean the same as everywhere else (see "One Vocabulary" in CONCEPT.md) and run immediately - an Option is a value, not a pipeline.

Examples

const first = [1, 2, 3].first()
print(first ?? 0)

The two cases are what a match covers, and neither of them needs a type in front of it: the prelude imports both.

const found: Int? = None
const text = match found {
  Some(value) => "{value}"
  None => "nothing"
}
print text

Pitfalls

  • An Option<Option<Value>> is two questions and not one. Option.flatMap is what answers both at once, which is also why a lookup that returns an Option is chained with flatMap and not with map.

Open

There is no combinator that takes a second Option (or, zip): two of them are combined with Option.flatMap and a closure.

Related

  • Option.orElse - the ?? operator, and the only way to leave the type.
  • Result - what to return instead when the absence has a reason.

case Some

case Some(value: Value)

The value is there.

case None

case None

There is no value.

fn isSome

fn isSome(): Bool

Whether there is a value. For the value itself, match or ?? instead.

fn isNone

fn isNone(): Bool

Whether the value is absent.

fn map

fn map<Output>(transform: Transform<Value, Output>): Output?

Changes the value if there is one, and keeps the absence if there is none.

Examples

const name: String? = Some "ada"
print name.map({ _.toUpperCase() })

fn flatMap

fn flatMap<Output>(transform: Transform<Value, Output?>): Output?

Changes the value into another Option and keeps one level: for a step that can answer with nothing itself.

Examples

const names = ["ada": "Ada Lovelace"]
const initial = names.get("ada").flatMap({ _.chars().first() })
print initial

fn filter

fn filter(predicate: Predicate<Value>): Value?

Keeps the value only if it answers the question, and turns it into None if it does not.

fn forEach

fn forEach(action: Action<Value>)

Runs the action if there is a value: the statement form of Option.map, for a step that answers with nothing.

fn toList

fn toList(): List<Value>

Into the world of pipelines: a list with no or one element.

fn orElse

fn orElse(fallback: lazy Value): Value

The value, or the fallback if there is none. This is the ?? operator, and the way out of the type.

The fallback is lazy, so it is only evaluated when it is needed: a fallback that reads a file or counts costs nothing while the value is there.

Examples

const missing: Int? = None
print missing.orElse(0)

fn okOr

fn okOr<Failure>(error: lazy Failure): Result<Value, Failure>

Gives the absence a reason and makes it a Result, which is what a function that answers with ? needs.

Examples

const found: Int? = None
print found.okOr("there is no number")

fn expect

fn expect(message: String): Value

The value, with the message as the reason if there is none. For a place where the absence is a bug.

Panics

When there is no value. Nothing else runs after that, so Option.orElse or Option.okOr is what a program that has to carry on uses instead.

extend Option<Value> with Show

extend<Value: Show> Option<Value> with Show

Some(value) or the bare None - a case with fields is written out, a case without one is its name.

fn show

fn show(): String

extend Option<Target> with From<Iterate<Value?>>

extend<Value, Target: From<Iterate<Value>>> Option<Target> with From<Iterate<Value?>>

All or nothing: names.map(findUser).to<List<User>?>() is Some(users) if every user was found. Stops pulling at the first None.

fn from

static fn from(value: Iterate<Value?>): Target?