Optional Values
The option
utility can be used to represent values that may or may not exist, akin to using nil
to represent values that do not exist.
However, using nil
to represent non-existing values can be error-prone, and be harder to work with. The option
utility provides a simpler and safer way to work with optional values.
Creating options
An option can be in two states: Some
or None
. The Some
state represents a value that exists, and the None
state represents a value that does not exist.
To create a Some
option, we can use Option.Some
:
local opt = Option.Some(5)
An option can contain multiple values:
local opt = Option.Some(5, "hello", true)
To create a None
option, we can use Option.None
:
local opt = Option.None
Accessing the inner values
There are multiple ways to access the inner values of an option.
The easiest way is to use the unwrap
method, which will return the inner values if it exist, or throw an error if it does not:
local some = Option.Some(5)
print(some:unwrap()) -- 5
local none = Option.None
none:unwrap() -- throws "called `Option.unwrap()` on a `None` value"
If you need to pass a custom error message, you can use the expect
method:
local none = Option.None
print(none:expect("expected a value")) -- throws "expected a value"
Sometimes, you want to get the inner value but use a default value if it does not exist. You can use the unwrapOr
method for this:
local some = Option.Some(5)
print(some:unwrapOr(10)) -- 5
local none = Option.None
print(none:unwrapOr(10)) -- 10
The value passed to unwrapOr
is eagerly evaluated, so it will be evaluated even if the option is Some
. If you need to lazily evaluate the default value, you can use the unwrapOrElse
method:
local some = Option.Some(5)
print(some:unwrapOrElse(function()
return 10
end)) -- 5
However, there are some cases where you don't want to throw an error or use a default value, but instead want to perform different actions based on whether the option is Some
or None
. You can use the match
method for this:
local some = Option.Some(5)
some:match({
Some = function(value)
print("got a value:", value)
end,
None = function()
print("got no value")
end,
})
Options require you to think about the case where the value does not exist, which can help you write more robust code.
Transforming options
Aside from accessing the inner value, you can also apply transformations to options and return new options.
You can transform the inner value of an option using the map
method:
local some = Option.Some(5)
local doubled = some:map(function(value)
return value * 2
end)
The Some
value will be doubles, and the None
will be preserved.
You can also use the andThen
method to transform the inner value of an option and return a new option:
local some = Option.Some(5)
local doubled = some:andThen(function(value)
if value % 2 == 0 then
return Option.Some(value * 2)
else
return Option.None
end
end)
If the Some
value is even, it will be doubled. Otherwise, None
will be returned. If the option is None
, it will be preserved.
You can also use the match
method from earlier by returning a value from the callbacks:
local some = Option.Some(5)
local result = some:match({
Some = function(value)
return value * 2
end,
None = function()
return 0
end,
})
print(result) -- 10
You can also filter the inner value of an option using the filter
method:
local some = Option.Some(5)
local even = some:filter(function(value)
return value % 2 == 0
end)
The value will be preserved if the option is Some
and the value is even, and the None
state will be returned if the option is None
or the value is odd.
TYPECHECKING
Luau fails to infer function parameters when passing functions to methods when calling them using :
syntax. Learn more.
Joining options
Sometimes you have multiple options and you want to combine them into a single option.
For example, lets say you had two options, and you wanted to get the sum of their values. You may achieve this using the andThen
and map
methods:
local optA = Option.Some(5)
local optB = Option.Some(10)
local sum = optA:andThen(function(a)
return optB:map(function(b)
return a + b
end)
end)
print(sum) -- Option::Some(15)
However, this introduces multiple levels of nesting, which may be undesirable.
Instead, we can use the join
method to turn both options into a single option, which we can map over:
local optA = Option.Some(5)
local optB = Option.Some(10)
local sum = optA:join(optB):map(function(a, b)
return a + b
end)
If either option is None
, the result will be None
.
Learn more
There are more methods available on the Option
type, this was just a small tour of what you can do with options. If you want to learn more, see the reference for more information.