Option Types
Option types are useful when you
want to internalize reasoning about partial functions. A simple motivating
example is division over integers, for which n/0
is undefined.
The basic idea is as follows: given a partial function f : A -> B
, we form the
type Option(B)
by extending B
with an element representing a missing value,
None
, and lift each value b
in B
to Some(b)
, allowing us to represent
the partial function pf : A -> Option(B)
, such that, for each a
in A
,
pf(a) = Some(f(a))
iff f(a)
is defined, and None
otherwise.
Apalache leverages its support for variants to define a polymorphic option type along with some common utility functions in the module Option.tla.
The module defines a type alias $option
as
\* @typeAlias: option = Some(a) | None(UNIT);
However, due to the current lack of support for polymorphic aliases, this alias
has limited utility, and parametric option types can only be properly expressed
by writing out the full variant type Some(a) | None(UNIT)
.
Nonetheless, in this manual page, we will sometimes write $option(a)
as a shorthand for the type Some(a) | None(UNIT)
.
In the context of TLA+, our encoding of option types is generalized over
"partial operators", meaning operators which return a value of type
$option(a)
. Support for partial functions is supplied by two operators,
OptionPartialFun and OptionFunApp.
Operators
Constructing present optional values
Notation: Some(v)
LaTeX notation: same
Apalache type: (a) => Some(a) | None(UNIT)
, for some type a
.
Arguments: The value v
of type a
to be lifted into the
option.
Effect: Produces a new value of the optional type.
Determinism: Deterministic.
Errors: No errors.
Example in TLA+:
\* @type: Some(Int) | None(UNIT);
SomeInt == Some(42)
Constructing absent optional values
Notation: None
LaTeX notation: same
Apalache type: Some(a) | None(UNIT)
, for some type a
.
Arguments: None
Effect: Produces a representation of an absent value.
Determinism: Deterministic.
Errors: No errors.
Example in TLA+:
\* @type: Some(Int) | None(UNIT);
NoInt == None
Checking for presence or absence of a value
Notation: IsSome(o)
or IsNone(o)
LaTeX notation: same
Apalache type: (Some(a) | None(UNIT)) => Bool
, for some type a
.
Arguments: One argument: a value of type $option(a)
for some type a
.
Effect: These operators are TRUE
or FALSE
depending on whether the
optional value is present or absent, in the expected way.
Determinism: Deterministic.
Errors: No errors.
Example in TLA+:
TRUE = IsSome(Some(5)) /\ IsNone(None)
Case analysis and elimination of optional values
Notation: OptionCase(o, caseSome, caseNone)
LaTeX notation: same
Apalache type: (Some(a) | None(UNIT), a => b, UNIT => b) => b
,
for some types a
and b
.
Arguments:
o
an optional valuecaseSome
is an operator to be applied to a present valuecaseNone
is an operator to be applied to theUNIT
if the value is absent
Effect: OptionCase(o, caseSome, caseNone)
is caseSome(v)
if o = Some(v)
, or else caseNone(UNIT)
. This is a way of eliminating a value of type
Option(a)
to produce a value of type b
.
Determinism: Deterministic.
Errors: No errors.
Example in TLA+:
/\ LET
\* @type: Int => Int;
caseSome(x) == x + 1
IN LET
\* @type: UNIT => Int;
caseNone(u) == 0
IN
OptionCase(Some(3), caseSome, caseNone) = 4
/\ LET
\* @type: Int => Str;
caseSome(x) == "Some Number"
IN LET
\* @type: UNIT => Str;
caseNone(u) == "None"
IN
OptionCase(None, caseSome, caseNone) = "None"
Sequencing application of partial operators
Notation: OptionFlatMap(f, o)
LaTeX notation: same
Apalache type:
(a => Some(b) | None(UNIT), Some(a) | None(UNIT)) => Some(b) | None(UNIT)
,
for some types a
and b
.
Arguments:
f
is a partial operatoro
an optional value
Effect: OptionFlatMap(f, o)
is f(v)
if o = Some(v)
, or else None
.
Determinism: Deterministic.
Errors: No errors.
Example in TLA+:
LET incr(n) == Some(n + 1) IN
LET fail(n) == None IN
LET q == OptionFlatMap(incr, Some(1)) IN
LET r == OptionFlatMap(incr, q) IN
LET s == OptionFlatMap(fail, r) IN
/\ r = Some(3)
/\ s = None
Unwrapping optional values
Notation: OptionGetOrElse(o, default)
LaTeX notation: same
Apalache type: (Some(a) | None(UNIT), a) => a
, for some type a
.
Arguments:
o
an optional valuedefault
is a default value to return
Effect:
OptionGetOrElse(o, default)
is v
iff o = Some(v)
, or else default
.
Determinism: Deterministic.
Errors: No errors.
Example in TLA+:
\* @type: Set(Int) => Some(Int) | None(UNIT);
MaxSet(s) ==
LET max(oa, b) ==
IF OptionGetOrElse(oa, b) > b
THEN oa
ELSE Some(b)
IN
ApaFoldSet(max, None, s)
Converting optional values
Converting to sequences
Notation: OptionToSeq(o)
LaTeX notation: same
Apalache type: (Some(a) | None(UNIT)) => Seq(a)
, for some type a
.
Arguments:
o
an optional value
Effect: OptionToSeq(o)
is <<v>>
iff o = Some(v)
, or else <<>>
.
Determinism: Deterministic.
Errors: No errors.
Example in TLA+:
LET \* @type: Seq(Int);
empty == <<>>
IN
/\ OptionToSeq(None) = empty
/\ OptionToSeq(Some(1)) = <<1>>
Converting to sets
Notation: OptionToSet(o)
LaTeX notation: same
Apalache type: (Some(a) | None(UNIT)) => Seq(a)
, for some type a
.
Arguments:
o
an optional value
Effect: OptionToSet(o)
is like OptionToSeq
, but producing a set.
Determinism: Deterministic.
Errors: No errors.
Example in TLA+:
LET \* @type: Set(Int);
empty == {}
IN
/\ OptionToSet(None) = empty
/\ OptionToSet(Some(1)) = {1}
Obtaining an optional value from a set
Notation: OptionGuess(s)
LaTeX notation: same
Apalache type: Set(a) => Some(a) | None(UNIT)
, for some type a
.
Arguments:
s
is a set
Effect: OptionGuess(s)
is None
if s = {}
, otherwise it is Some(x)
,
where x \in s
. x
is selected from s
nondeterministically.
Determinism: Nondeterministic.
Errors: No errors.
Example in TLA+:
LET
\* @type: Set(Int);
empty == {}
IN
/\ OptionGuess(empty) = None
/\ LET choices == {1,2,3,4} IN
LET choice == OptionGuess(choices) IN
VariantGetUnsafe("Some", choice) \in choices
Apply a function to a partial value
Notation: OptionFunApp(f, o)
LaTeX notation: same
Apalache type: (a -> b, Some(a) | None(UNIT)) => Some(b) | None(UNIT)
Arguments:
f
is a functiono
is an optional value
Effect:
OptionFunApp(f, o)
is Some(f[v])
if o = Some(v)
or else None
.
Determinism: Deterministic.
Errors: No errors.
Example in TLA+:
LET f == [x \in 1..3 |-> x + 1] IN
/\ OptionFunApp(f, Some(1)) = Some(2)
/\ OptionFunApp(f, None) = None
Extend a total function into a partial function
Notation: OptionPartialFun(f, undef)
LaTeX notation: same
Apalache type: (a -> b, Set(a)) => (a -> Some(b) | None(UNIT))
Arguments:
f
is a total functionundef
is a set of values for which the new function is to be "undefined"
Effect: OptionPartialFun(f, undef)
is a function mapping each value in
undef
to None
, and each value x \in (DOMAIN f \ undef)
to Some(f[x])
.
This can be used to extend a total function into a "partial function"
whose domain is extended to include the values in 'undef'.
Determinism: Deterministic.
Errors: No errors.
Example in TLA+:
LET def == 1..3 IN
LET undef == 4..10 IN
LET f == [x \in def |-> x + 1] IN
LET pf == OptionPartialFun(f, undef) IN
/\ \A n \in def: pf[n] = Some(n + 1)
/\ \A n \in undef: pf[n] = None