silk.core.option.
Optional owned values that are present or absent, with no explanation for the absence.
When to use#
Use Option when "no value" is a normal answer, such as a lookup that finds nothing. Use
silk.core.result.Result when the caller must know why the value is missing.
Use map to transform a present value, flatMap when the next step can also produce no
value, and unwrapOr to get a plain value with a fallback. Use match when the present and
absent cases need different custom behavior.
Details#
Option<T> is a union with the variants Some and None. Each combinator consumes the
option. A present value moves into the result. A value that the operation does not use is
dropped. No combinator copies T, so T does not need to be Copy.
Examples#
Look up a port and fall back to a default#
import silk.core.option { Option }
fn portFor(scheme: string) -> Option<i32> {
if scheme == "http" {
return Option<i32>.some(80)
}
return Option<i32>.none()
}
fn secure(port: i32) -> Option<i32> {
if port == 80 {
return Option<i32>.some(443)
}
return Option<i32>.none()
}
pub fn main() -> i32 {
let known = portFor("http") |> Option<i32>.flatMap<i32>(secure) |> Option<i32>.unwrapOr(8080)
let unknown = portFor("gopher") |> Option<i32>.flatMap<i32>(secure) |> Option<i32>.unwrapOr(8080)
if known != 443 || unknown != 8080 {
return 1
}
return 0
}Make a service optional#
An effect that requires &Option<&S> runs with or without a provider of S. See withSome.
import silk.core.effect { Effect }
import silk.core.option { Option, provideSome, withSome }
service Tracer {
effect fn trace(value: i32) -> i32 ? &Tracer
}
struct Offset {
amount: i32,
}
impl Tracer for Offset {
effect fn trace(self: &Self, value: i32) -> i32 {
return value + self.amount
}
}
effect fn plain() -> i32 {
return 1
}
effect fn traced() -> i32 ? &Tracer {
return run Tracer.trace(1)
}
effect fn measure() -> i32 ? &Option<&Tracer> {
return run withSome<&Tracer>(plain(), traced())
}
pub fn main() -> i32 {
let offset = Offset {amount: 10}
let none = Option<never>.none()
let present = run provideSome<&Tracer>(measure(), &offset)
let absent = run Effect.provide(measure(), &none)
if present != 11 || absent != 1 {
return 1
}
return 0
}Import as Option with import silk.core.option { Option }.
Public declarations: 5.
Option#
pub union Option<T>An owned value of type T that is present (Some) or absent (None).
When to use#
Use Option as a return type when "no value" is a normal result and needs no reason. Use
silk.core.result.Result when the caller must know why the value is missing.
Details#
Create values with some and none. Test them with isSome and isNone. Use map,
flatMap, and unwrapOr for the common cases. Use match when the two cases need
different custom behavior. The option owns its value, so dropping a present option also drops
the value.
In a requirement row, &Option<&S> is an optional service: the effect runs with or without a
provider of S. Branch on it with withSome.
Examples#
Handle both cases with match#
import silk.core.option { Option }
fn describe(found: Option<i32>) -> i32 {
return match move found {
Option<i32>.Some { value } => value
Option<i32>.None => -1
}
}
pub fn main() -> i32 {
let found = describe(Option<i32>.some(3))
let missing = describe(Option<i32>.none())
if found != 3 || missing != -1 {
return 1
}
return 0
}None#
NoneThe absent case. It carries no value and no reason for the absence.
Some#
SomeThe present case. It owns one value of type T.
Field value#
pub value: TThe owned present value.
Implementation Copy for Option<T>#
impl<T: Copy> Copy for Option<T>An Option of a Copy type is Copy, so a struct with an Option field can be Copy too.
Implementation Option<T>#
impl<T> Option<T>Associated function none#
pub fn none() -> Option<T>Returns an absent Option<T>.
Details#
The result holds no value and allocates no memory. Write the element type explicitly, as in
Option<i32>.none(), because no argument gives the compiler that type.
Associated function some#
pub fn some(value: T) -> Option<T>Returns a present Option<T> that owns value.
Details#
This function moves value into the option. The caller can no longer use the original
binding.
Method isSome#
pub fn isSome(self: &Self) -> boolReturns true if self holds a value.
When to use#
Use isSome when only the presence matters. Use match or unwrapOr to read the value.
Details#
This function borrows self, so the option and its value stay available.
Examples#
Test an option and keep it#
import silk.core.option { Option }
pub fn main() -> i32 {
let found = Option<i32>.some(3)
if !Option<i32>.isSome(&found) {
return 1
}
let value = move found |> Option<i32>.unwrapOr(0)
return value - 3
}Method isNone#
pub fn isNone(self: &Self) -> boolReturns true if self holds no value.
When to use#
Use isNone when only the absence matters, such as to stop a search early. Use match or
unwrapOr to read a present value.
Details#
This function borrows self, so the option and its value stay available.
Examples#
Test an absent option#
import silk.core.option { Option }
pub fn main() -> i32 {
let missing = Option<i32>.none()
if !Option<i32>.isNone(&missing) {
return 1
}
return 0
}Method map#
pub fn map<U>(self: Self, transform: once fn(T) -> U) -> Option<U>Returns transform(value) as a present option if self is present. Returns an absent option
if self is absent.
When to use#
Use map when the transform always succeeds. Use flatMap when the transform can also
produce no value.
Details#
transform runs one time for a present value and does not run for an absent value. This
function consumes self and moves the present value into transform.
Gotchas#
The original option is not available after the call. To read the value and keep the option,
use match on a borrow of the option.
Examples#
Double a present value#
import silk.core.option { Option }
fn double(value: i32) -> i32 {
return value * 2
}
pub fn main() -> i32 {
let doubled = Option<i32>.some(21) |> Option<i32>.map<i32>(double) |> Option<i32>.unwrapOr(0)
if doubled != 42 {
return 1
}
return 0
}Method flatMap#
pub fn flatMap<U>(self: Self, transform: once fn(T) -> Option<U>) -> Option<U>Returns the option that transform produces for a present value. Returns an absent option if
self is absent.
When to use#
Use flatMap to chain steps where each step can produce no value. The result is one
Option<U>, not a nested Option<Option<U>>. Use map when the step always produces a
value.
Details#
transform runs one time for a present value and does not run for an absent value. This
function consumes self and moves the present value into transform.
Examples#
Reject a value in the second step#
import silk.core.option { Option }
fn positive(value: i32) -> Option<i32> {
if value > 0 {
return Option<i32>.some(value)
}
return Option<i32>.none()
}
pub fn main() -> i32 {
let kept = Option<i32>.some(5) |> Option<i32>.flatMap<i32>(positive) |> Option<i32>.unwrapOr(0)
let rejected = Option<i32>.some(-5) |> Option<i32>.flatMap<i32>(positive)
|> Option<i32>.unwrapOr(0)
if kept != 5 || rejected != 0 {
return 1
}
return 0
}Method unwrapOr#
pub fn unwrapOr(
self: Self,
/// The value to return if `self` is absent. It is dropped if `self` is present.
fallback: T,
) -> TReturns the present value. Returns fallback if self is absent.
When to use#
Use unwrapOr at the end of an option chain when a default value is correct for the absent
case. Use match when the absent case needs different control flow, such as an early return.
Details#
This function consumes self and fallback. It returns one of the two values and drops the
other value.
Gotchas#
The caller evaluates fallback before the call, also when self is present. If the fallback
is expensive to make, use match and make the fallback only in the absent case.
Examples#
Use a present value and an absent value#
import silk.core.option { Option }
pub fn main() -> i32 {
let present = Option<i32>.some(7)
let absent = Option<i32>.none()
let first = move present |> Option<i32>.unwrapOr(0)
let second = move absent |> Option<i32>.unwrapOr(5)
if first != 7 || second != 5 {
return 1
}
return 0
}Parameter fallback#
fallback: TThe value to return if self is absent. It is dropped if self is present.
OptionService#
pub service OptionService<?S>The service that an &Option<&S> requirement names.
When to use#
Do not name this service in your code. Write ? &Option<&S> in a requirement row and branch
with withSome. Diagnostics name this service where they name the key.
Details#
Option<&S> is an ordinary requirement key, separate from S. An Option<P> provides it when
&P provides S: Option<P>.some(provider) is present and Option<P>.None is absent. The
explicit absent provider is Option<never>.none(), because never serves every service and has
no values. provideSome lifts a borrowed provider of S into a present one.
Operation select#
effect<'env> fn select<'env, A, E, ?R>(
absent: once Effect<'env; A ! E ? R>,
present: once Effect<'env; A ! E ? R | S>,
) -> A ! E ? R | &OptionService<S>
where R in Without<R, S>Runs present with the provider of S when one exists, otherwise runs absent.
Implementation OptionService<S> for Option<P>#
impl<P, ?S> OptionService<S> for Option<P> where &P provides S from SAn Option serves an optional service: Some holds the provider and None has none.
Operation select#
effect<'env> fn select<'env, A, E, ?R>(
self: &Self,
absent: once Effect<'env; A ! E ? R>,
present: once Effect<'env; A ! E ? R | S>,
) -> A ! E ? R
where R in Without<R, S>Runs present with the provider in Some, or runs absent for None.
withSome#
pub effect fn withSome<'env, ?S, A, E, ?R>(
absent: once Effect<'env; A ! E ? R>,
present: once Effect<'env; A ! E ? R | S>,
) -> A ! E ? R | &OptionService<S>
where R in Without<R, S>Runs present when the optional service S has a provider, otherwise runs absent.
When to use#
Use this function inside an effect that requires &Option<&S>, where the work differs with
and without S, for example optional tracing or metrics.
Details#
The type argument is the requirement entry, such as withSome<&Tracer>. The provider decides
the branch at runtime with one tag check. The branch that does not run is dropped. Both branches
share the other requirements R, which must not contain S, and only present may use S.
Gotchas#
A provider of S does not satisfy &Option<&S>. Provide Option<P>.some(provider),
Option<never>.none(), or use provideSome.
Present#
pub struct Present<'env, P>A borrowed provider that always serves an optional service as present.
Details#
provideSome makes one for a single provision. Use Option<P> instead when the provider is
owned or when presence is decided at runtime.
Field provider#
pub provider: &'env PThe borrowed provider of the optional service.
Implementation OptionService<S> for Present<'env, P>#
impl<'env, P, ?S> OptionService<S> for Present<'env, P> where &'env P provides S from SA Present always runs the present branch with its borrowed provider.
Operation select#
effect<'run> fn select<'run, A, E, ?R>(
self: &Self,
absent: once Effect<'run; A ! E ? R>,
present: once Effect<'run; A ! E ? R | S>,
) -> A ! E ? R
where R in Without<R, S>Runs present with the borrowed provider and drops absent.
provideSome#
pub effect fn provideSome<'env, ?S, A, P, E, ?R>(
program: once Effect<'env; A ! E ? R>,
provider: &'env P,
) -> A ! E ? Without<R, OptionService<S>>
where &Present<'env, P> provides OptionService<S> from RProvides a borrowed provider of S to the optional requirement &Option<S> of program.
When to use#
Use this function when you have a provider of S and the effect requires &Option<S>. A
provider of S does not satisfy &Option<S> without this explicit lift.
Details#
The type argument is the requirement entry, such as provideSome<&Tracer>. The provider stays
borrowed for the execution of program. Every other requirement in R stays visible in the
result.
Category: Core.
Profiles: aarch64-apple-darwin, aarch64-unknown-linux-gnu, aarch64-unknown-linux-gnu-no-libc, x86_64-unknown-linux-gnu, x86_64-unknown-linux-gnu-no-libc, wasm32-unknown-unknown, wasm32-wasip1, wasm32-wasip2.