mirror of
https://github.com/CompeyDev/rusty-luau.git
synced 2024-12-12 12:50:40 +00:00
feat(Option): include documentation + minor changes
* Add proper equality checks for tables in metamethod. * Document all exported functions & classes.
This commit is contained in:
parent
8632d052a9
commit
d85faed644
1 changed files with 676 additions and 3 deletions
679
lib/option.luau
679
lib/option.luau
|
@ -1,17 +1,93 @@
|
||||||
|
local function tableEq(tbl1, tbl2)
|
||||||
|
if tbl1 == tbl2 then
|
||||||
|
return true
|
||||||
|
elseif type(tbl1) == "table" and type(tbl2) == "table" then
|
||||||
|
for key1, value1 in pairs(tbl1) do
|
||||||
|
local value2 = tbl2[key1]
|
||||||
|
|
||||||
|
if value2 == nil then
|
||||||
|
-- avoid the type call for missing keys in tbl2 by directly comparing with nil
|
||||||
|
return false
|
||||||
|
elseif value1 ~= value2 then
|
||||||
|
if type(value1) == "table" and type(value2) == "table" then
|
||||||
|
if not tableEq(value1, value2) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
else
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- check for missing keys in tbl1
|
||||||
|
for key2, _ in pairs(tbl2) do
|
||||||
|
if tbl1[key2] == nil then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@class Option
|
||||||
|
|
||||||
|
Type [Option] represents an optional value: every [Option] is either [Some] and contains a
|
||||||
|
value, or [None], and does not. Common uses of an [Option] may involve:
|
||||||
|
|
||||||
|
* Initial values
|
||||||
|
* Return values for functions that are not defined over their entire input range (partial functions)
|
||||||
|
* Return value for otherwise reporting simple errors, where None is returned on error
|
||||||
|
* Optional object fields
|
||||||
|
* Values that can be loaned or “taken”
|
||||||
|
* Optional function arguments
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function divide(numerator: number, denominator: number): Option<number>
|
||||||
|
if denominator == 0 then
|
||||||
|
None()
|
||||||
|
else
|
||||||
|
return Some(numerator / denominator)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
```
|
||||||
|
]=]
|
||||||
local Option = {}
|
local Option = {}
|
||||||
|
|
||||||
export type Option<T> = typeof(Option) & {
|
export type Option<T> = typeof(Option) & {
|
||||||
_optValue: T?,
|
_optValue: T?,
|
||||||
typeId: "Option",
|
typeId: "Option",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
No value.
|
||||||
|
]=]
|
||||||
function None<T>(): Option<T>
|
function None<T>(): Option<T>
|
||||||
return Option.new(nil) :: Option<T>
|
return Option.new(nil) :: Option<T>
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Some value of type `T`.
|
||||||
|
]=]
|
||||||
function Some<T>(val: T): Option<T>
|
function Some<T>(val: T): Option<T>
|
||||||
return Option.new(val) :: Option<T>
|
return Option.new(val) :: Option<T>
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Converts a potentially `nil` value into an [Option].
|
||||||
|
|
||||||
|
@param val T? -- The value to convert into an [Option]
|
||||||
|
@return Option<T>
|
||||||
|
]=]
|
||||||
function Option.new<T>(val: T?)
|
function Option.new<T>(val: T?)
|
||||||
return setmetatable(
|
return setmetatable(
|
||||||
{
|
{
|
||||||
|
@ -29,7 +105,11 @@ function Option.new<T>(val: T?)
|
||||||
end,
|
end,
|
||||||
|
|
||||||
__eq = function<T>(self: Option<T>, other: Option<T>): boolean
|
__eq = function<T>(self: Option<T>, other: Option<T>): boolean
|
||||||
return self._optValue == other._optValue
|
if typeof(self._optValue) == "table" and typeof(other._optValue) == "table" then
|
||||||
|
return tableEq(self._optValue, other._optValue)
|
||||||
|
else
|
||||||
|
return self._optValue == other._optValue
|
||||||
|
end
|
||||||
end,
|
end,
|
||||||
__lt = function<T>(self: Option<T>, other: Option<T>): boolean
|
__lt = function<T>(self: Option<T>, other: Option<T>): boolean
|
||||||
if self:isSome() and other:isSome() then
|
if self:isSome() and other:isSome() then
|
||||||
|
@ -51,19 +131,66 @@ function Option.new<T>(val: T?)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Returns `true` is the [Option] is an [Option:Some] value.
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@return boolean
|
||||||
|
]=]
|
||||||
function Option.isSome<T>(self: Option<T>): boolean
|
function Option.isSome<T>(self: Option<T>): boolean
|
||||||
return self._optValue ~= nil
|
return self._optValue ~= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Returns `true` is the [Option] is an [Option:Some] value and the value inside of it
|
||||||
|
matches a predicate.
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param op (val: T) -> boolean -- The predicate function
|
||||||
|
@return boolean
|
||||||
|
]=]
|
||||||
function Option.isSomeAnd<T>(self: Option<T>, op: (val: T) -> boolean): boolean
|
function Option.isSomeAnd<T>(self: Option<T>, op: (val: T) -> boolean): boolean
|
||||||
-- We know that it's not None, so this is fine
|
-- We know that it's not None, so this is fine
|
||||||
return self:isSome() and op(self._optValue :: T)
|
return self:isSome() and op(self._optValue :: T)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Returns `true` is the [Option] is an [Option:None] value.
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@return boolean
|
||||||
|
]=]
|
||||||
function Option.isNone<T>(self: Option<T>): boolean
|
function Option.isNone<T>(self: Option<T>): boolean
|
||||||
return not self:isSome()
|
return not self:isSome()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Returns the [Option:Some].
|
||||||
|
|
||||||
|
@error panic -- Panics if there is an [Option:None] with a custom panic message provided by `msg`.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local x: Option<string> = Some("value")
|
||||||
|
assert(x:expect("fruits are healthy") == "value")
|
||||||
|
```
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local x: Option<string> = None()
|
||||||
|
x:expect("fruits are healthy") -- panics with `fruits are healthy`
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param msg string -- The panic message
|
||||||
|
@return boolean
|
||||||
|
]=]
|
||||||
function Option.expect<T>(self: Option<T>, msg: string): T | never
|
function Option.expect<T>(self: Option<T>, msg: string): T | never
|
||||||
if self:isSome() then
|
if self:isSome() then
|
||||||
return self._optValue :: T
|
return self._optValue :: T
|
||||||
|
@ -72,6 +199,18 @@ function Option.expect<T>(self: Option<T>, msg: string): T | never
|
||||||
return error(`panic: {msg}; expected Option to be type Option::Some(T), got Option::None`)
|
return error(`panic: {msg}; expected Option to be type Option::Some(T), got Option::None`)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Returns the [Option:Some].
|
||||||
|
|
||||||
|
Because this function may panic, its use is generally discouraged. Instead, prefer to use
|
||||||
|
[Option:unwrapOr], [Option:unwrapOrElse], or [Option:unwrapOrDefault].
|
||||||
|
|
||||||
|
@error panic -- Panics if the self value is [Option:None].
|
||||||
|
@param self Option<T>
|
||||||
|
@return T | never
|
||||||
|
]=]
|
||||||
function Option.unwrap<T>(self: Option<T>): T | never
|
function Option.unwrap<T>(self: Option<T>): T | never
|
||||||
if self:isSome() then
|
if self:isSome() then
|
||||||
return self._optValue :: T
|
return self._optValue :: T
|
||||||
|
@ -80,6 +219,24 @@ function Option.unwrap<T>(self: Option<T>): T | never
|
||||||
return error("called `Option:unwrap()` on a `None` value")
|
return error("called `Option:unwrap()` on a `None` value")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Returns the contained [Option:Some] value or a provided default.
|
||||||
|
|
||||||
|
Arguments passed to unwrap_or are eagerly evaluated; if you are passing the result of
|
||||||
|
a function call, it is recommended to use [Option:unwrapOrElse], which is lazily
|
||||||
|
evaluated.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
assert(Some("car"):unwrapOr("bike") == "car")
|
||||||
|
assert(None():unwrapOr("bike") == "bike")
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param default T -- The default value
|
||||||
|
@return T
|
||||||
|
]=]
|
||||||
function Option.unwrapOr<T>(self: Option<T>, default: T): T
|
function Option.unwrapOr<T>(self: Option<T>, default: T): T
|
||||||
if self:isSome() then
|
if self:isSome() then
|
||||||
return self._optValue :: T
|
return self._optValue :: T
|
||||||
|
@ -88,6 +245,25 @@ function Option.unwrapOr<T>(self: Option<T>, default: T): T
|
||||||
return default
|
return default
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Returns the contained [Option:Some] value or computes it from a function.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local k = 10
|
||||||
|
assert(Some(4).unwrapOrElse(function()
|
||||||
|
return 2 * k
|
||||||
|
end) == 4)
|
||||||
|
assert(None().unwrapOrElse(function()
|
||||||
|
return 2 * k
|
||||||
|
end) == 20)
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param default () -> T -- The function which computes the default value
|
||||||
|
@return T
|
||||||
|
]=]
|
||||||
function Option.unwrapOrElse<T>(self: Option<T>, default: () -> T): T
|
function Option.unwrapOrElse<T>(self: Option<T>, default: () -> T): T
|
||||||
if self:isSome() then
|
if self:isSome() then
|
||||||
return self._optValue :: T
|
return self._optValue :: T
|
||||||
|
@ -96,6 +272,30 @@ function Option.unwrapOrElse<T>(self: Option<T>, default: () -> T): T
|
||||||
return default()
|
return default()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Maps an [Option]<T> to [Option]<U> by applying a function to a contained value
|
||||||
|
(if [Option:Some]) or returns [Option:None](if [Option:None]).
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local maybeSomeString: Option<string> = Some("Hello, World!")
|
||||||
|
|
||||||
|
local maybeSomeLen: Option<number> = maybeSomeString:map(function(s)
|
||||||
|
return #s
|
||||||
|
end)
|
||||||
|
assert(maybeSomeLen == 13)
|
||||||
|
|
||||||
|
local x: Option<string> = None()
|
||||||
|
assert(x:map(function(s)
|
||||||
|
return #s
|
||||||
|
end) == None())
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param op (x: T) -> U? -- The function to apply
|
||||||
|
@return Option<U>
|
||||||
|
]=]
|
||||||
function Option.map<T, U>(self: Option<T>, op: (x: T) -> U?): Option<U>
|
function Option.map<T, U>(self: Option<T>, op: (x: T) -> U?): Option<U>
|
||||||
if self:isSome() then
|
if self:isSome() then
|
||||||
local val = op(self._optValue :: T)
|
local val = op(self._optValue :: T)
|
||||||
|
@ -110,7 +310,30 @@ function Option.map<T, U>(self: Option<T>, op: (x: T) -> U?): Option<U>
|
||||||
return None()
|
return None()
|
||||||
end
|
end
|
||||||
|
|
||||||
function Option.inspect<T>(self: Option<T>, op: (x: T) -> nil): Option<T>
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Calls the provided closure with the contained value (if [Option:Some]).
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local v = { 1, 2, 3, 4, 5 }
|
||||||
|
|
||||||
|
-- prints "got: 4"
|
||||||
|
local x: Option<number> = Option.new(v[4]):inspect(function(x)
|
||||||
|
print("got: " .. x)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- prints nothing
|
||||||
|
local x: Option<number> = Option.new(v[5]):inspect(function(x)
|
||||||
|
print("got: " .. x)
|
||||||
|
end)
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param op (x: T) -> () -- The function to call
|
||||||
|
@return Option<T>
|
||||||
|
]=]
|
||||||
|
function Option.inspect<T>(self: Option<T>, op: (x: T) -> ()): Option<T>
|
||||||
if self:isSome() then
|
if self:isSome() then
|
||||||
op(self._optValue :: T)
|
op(self._optValue :: T)
|
||||||
end
|
end
|
||||||
|
@ -118,6 +341,28 @@ function Option.inspect<T>(self: Option<T>, op: (x: T) -> nil): Option<T>
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Returns the provided default result (if none), or applies a function to
|
||||||
|
the contained value (if any).
|
||||||
|
|
||||||
|
Arguments passed to [Option:mapOr] are eagerly evaluated; if you are passing the
|
||||||
|
result of a function call, it is recommended to use [Option:mapOrElse],
|
||||||
|
which is lazily evaluated.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local x: Option<string> = Some("foo")
|
||||||
|
assert(x.mapOr(42, function(v) return #v end) == 3)
|
||||||
|
|
||||||
|
local x: Option<string> = None()
|
||||||
|
assert(x.mapOr(42, function(v) return #v end) == 42)
|
||||||
|
```
|
||||||
|
@param self Option<T>
|
||||||
|
@param default U -- The default value
|
||||||
|
@param op (val: T) -> U -- The function to apply
|
||||||
|
@return Option<T>
|
||||||
|
]=]
|
||||||
function Option.mapOr<T, U>(self: Option<T>, default: U, op: (val: T) -> U)
|
function Option.mapOr<T, U>(self: Option<T>, default: U, op: (val: T) -> U)
|
||||||
if self:isSome() then
|
if self:isSome() then
|
||||||
return op(self._optValue :: T)
|
return op(self._optValue :: T)
|
||||||
|
@ -126,6 +371,37 @@ function Option.mapOr<T, U>(self: Option<T>, default: U, op: (val: T) -> U)
|
||||||
return default
|
return default
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Computes a default function result (if none), or applies a different function
|
||||||
|
to the contained value (if any).
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local k = 21;
|
||||||
|
|
||||||
|
local x: Option<string> = Some("foo")
|
||||||
|
assert(
|
||||||
|
x:mapOrElse(
|
||||||
|
function() return 2 * k end,
|
||||||
|
function(v) return #v end
|
||||||
|
) == 3
|
||||||
|
)
|
||||||
|
|
||||||
|
local x: Option<string> = None()
|
||||||
|
assert(
|
||||||
|
x:mapOrElse(
|
||||||
|
function() return 2 * k end,
|
||||||
|
function(v) return #v end
|
||||||
|
) == 42
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param default () -> U -- The function to compute the default value
|
||||||
|
@param op (val: T) -> U -- The function to apply
|
||||||
|
@return Option<T>
|
||||||
|
]=]
|
||||||
function Option.mapOrElse<T, U>(self: Option<T>, default: () -> U, op: (val: T) -> U): U
|
function Option.mapOrElse<T, U>(self: Option<T>, default: () -> U, op: (val: T) -> U): U
|
||||||
if self:isSome() then
|
if self:isSome() then
|
||||||
return op(self._optValue :: T)
|
return op(self._optValue :: T)
|
||||||
|
@ -135,8 +411,40 @@ function Option.mapOrElse<T, U>(self: Option<T>, default: () -> U, op: (val: T)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- TODO: Iterator traits
|
-- TODO: Iterator traits
|
||||||
function Option.iter() end
|
function Option.iter(): never
|
||||||
|
return error("Unimplemented: `Option:iter()`")
|
||||||
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Returns [Option:None] if the option is [Option:None], otherwise returns `optb`.
|
||||||
|
|
||||||
|
Arguments passed to and are eagerly evaluated; if you are passing the result of a
|
||||||
|
function call, it is recommended to use [Option:andThen], which is lazily evaluated.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local x: Option<number> = Some(2)
|
||||||
|
local y: Option<String> = None()
|
||||||
|
assert(x:and_(y) == None())
|
||||||
|
|
||||||
|
local x: Option<number> = None()
|
||||||
|
local y: Option<string> = Some("foo")
|
||||||
|
assert(x:and_(y) == None())
|
||||||
|
|
||||||
|
local x: Option<number> = Some(2)
|
||||||
|
local y: Option<string> = Some("foo")
|
||||||
|
assert(x:and_(y) == Some("foo"))
|
||||||
|
|
||||||
|
local x: Option<u32> = None()
|
||||||
|
local y: Option<string> = None()
|
||||||
|
assert(x:and_(y), None())
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param optb Option<U> -- The other option
|
||||||
|
@return Option<U>
|
||||||
|
]=]
|
||||||
function Option.and_<T, U>(self: Option<T>, optb: Option<U>): Option<U>
|
function Option.and_<T, U>(self: Option<T>, optb: Option<U>): Option<U>
|
||||||
if self:isSome() then
|
if self:isSome() then
|
||||||
return optb
|
return optb
|
||||||
|
@ -145,6 +453,45 @@ function Option.and_<T, U>(self: Option<T>, optb: Option<U>): Option<U>
|
||||||
return None()
|
return None()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Returns [Option:None] if the option is [Option:None], otherwise calls `op` with the wrapped
|
||||||
|
value and returns the result.
|
||||||
|
|
||||||
|
Some languages call this operation flatmap.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function sqThenToString(x: number): Option<string>
|
||||||
|
return Option.new(x ^ 2):map(function(sq)
|
||||||
|
return tostring(sq)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(Some(2):andThen(sqThenToString) == Some(tostring(4)))
|
||||||
|
assert(None():andThen(sqThenToString) == None())
|
||||||
|
```
|
||||||
|
|
||||||
|
Often used to chain fallible operations that may return [Option:None].
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local arr2d = { { "A0", "A1" }, { "B0", "B1" } }
|
||||||
|
|
||||||
|
local item01: Option<string> = Option.new(arr2d[1]):andThen(function(row)
|
||||||
|
return row[2]
|
||||||
|
end)
|
||||||
|
assert(item01 == Some("A1"))
|
||||||
|
|
||||||
|
local item20: Option<string> = Option.new(arr2d[3]):andThen(function(row)
|
||||||
|
return row[0]
|
||||||
|
end)
|
||||||
|
assert(item20 == None())
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param op (val: T) -> Option<U> -- The function to call
|
||||||
|
@return Option<U>
|
||||||
|
]=]
|
||||||
function Option.andThen<T, U>(self: Option<T>, op: (val: T) -> Option<U>): Option<U>
|
function Option.andThen<T, U>(self: Option<T>, op: (val: T) -> Option<U>): Option<U>
|
||||||
if self:isSome() then
|
if self:isSome() then
|
||||||
return op(self._optValue :: T)
|
return op(self._optValue :: T)
|
||||||
|
@ -153,6 +500,29 @@ function Option.andThen<T, U>(self: Option<T>, op: (val: T) -> Option<U>): Optio
|
||||||
return None()
|
return None()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Returns [Option:None] if the option is [Option:None], otherwise calls `predicate` with
|
||||||
|
the wrapped value and returns:
|
||||||
|
|
||||||
|
* [Option:Some](t) if predicate returns `true` (where `t` is the wrapped value), and
|
||||||
|
* [Option:None] if predicate returns `false`.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function isEven(n: number): boolean
|
||||||
|
return n % 2 == 0
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(None():filter(isEven) == None())
|
||||||
|
assert(Some(3):filter(isEven) == None())
|
||||||
|
assert(Some(4):filter(isEven) == Some(4))
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param predicate (val: T) -> boolean -- The predicate function which must match an element
|
||||||
|
@return Option<T>
|
||||||
|
]=]
|
||||||
function Option.filter<T>(self: Option<T>, predicate: (val: T) -> boolean): Option<T>
|
function Option.filter<T>(self: Option<T>, predicate: (val: T) -> boolean): Option<T>
|
||||||
if self:isSome() then
|
if self:isSome() then
|
||||||
if predicate(self._optValue :: T) then
|
if predicate(self._optValue :: T) then
|
||||||
|
@ -163,6 +533,37 @@ function Option.filter<T>(self: Option<T>, predicate: (val: T) -> boolean): Opti
|
||||||
return None()
|
return None()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Returns the option if it contains a value, otherwise returns `optb`.
|
||||||
|
|
||||||
|
Arguments passed to [Option:or_] are eagerly evaluated; if you are passing the result of
|
||||||
|
a function call, it is recommended to use [Option:orElse], which is lazily
|
||||||
|
evaluated.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local x: Option<number> = Some(2)
|
||||||
|
local y: Option<number> = None()
|
||||||
|
assert(x:or_(y) == Some(2))
|
||||||
|
|
||||||
|
local x: Option<number> = None()
|
||||||
|
local y: Option<number> = Some(100)
|
||||||
|
assert(x:or_(y) == Some(100))
|
||||||
|
|
||||||
|
local x: Option<number> = Some(2)
|
||||||
|
local y: Option<number> = Some(100)
|
||||||
|
assert(x:or_(y) == Some(2))
|
||||||
|
|
||||||
|
local x: Option<number> = None()
|
||||||
|
local y: Option<number> = None()
|
||||||
|
assert(x:or_(y), None())
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param optb Option<T> -- The other option
|
||||||
|
@return Option<T>
|
||||||
|
]=]
|
||||||
function Option.or_<T>(self: Option<T>, optb: Option<T>): Option<T>
|
function Option.or_<T>(self: Option<T>, optb: Option<T>): Option<T>
|
||||||
if self:isSome() then
|
if self:isSome() then
|
||||||
return self
|
return self
|
||||||
|
@ -171,6 +572,28 @@ function Option.or_<T>(self: Option<T>, optb: Option<T>): Option<T>
|
||||||
return optb
|
return optb
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Returns the option if it contains a value, otherwise calls `op` and returns the result.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
function nobody(): Option<string>
|
||||||
|
return None()
|
||||||
|
end
|
||||||
|
function vikings(): Option<string>
|
||||||
|
return Some("vikings")
|
||||||
|
end
|
||||||
|
|
||||||
|
assert(Some("barbarians"):orElse(vikings) == Some("barbarians"))
|
||||||
|
assert(None():orElse(vikings) == Some("vikings"))
|
||||||
|
assert(None():orElse(nobody) == None())
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param op () -> Option<T> -- The function to call
|
||||||
|
@return Option<T>
|
||||||
|
]=]
|
||||||
function Option.orElse<T>(self: Option<T>, op: () -> Option<T>): Option<T>
|
function Option.orElse<T>(self: Option<T>, op: () -> Option<T>): Option<T>
|
||||||
if self:isSome() then
|
if self:isSome() then
|
||||||
return self
|
return self
|
||||||
|
@ -179,6 +602,34 @@ function Option.orElse<T>(self: Option<T>, op: () -> Option<T>): Option<T>
|
||||||
return op()
|
return op()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Returns [Option:Some] if exactly one of `self`, `optb` is [Option:Some],
|
||||||
|
otherwise returns [Option:None].
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local x: Option<number> = Some(2)
|
||||||
|
local y: Option<number> = None()
|
||||||
|
assert(x:xor(y) == Some(2))
|
||||||
|
|
||||||
|
local x: Option<number> = None()
|
||||||
|
local y: Option<number> = Some(2)
|
||||||
|
assert(x:xor(y) == Some(2))
|
||||||
|
|
||||||
|
local x: Option<number> = Some(2)
|
||||||
|
local y: Option<number> = Some(2)
|
||||||
|
assert(x:xor(y) == None())
|
||||||
|
|
||||||
|
local x: Option<number> = None()
|
||||||
|
local y: Option<number> = None()
|
||||||
|
assert(x:xor(y) == None())
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param optb Option<T> -- The other option
|
||||||
|
@return Option<T>
|
||||||
|
]=]
|
||||||
function Option.xor<T>(self: Option<T>, optb: Option<T>): Option<T>
|
function Option.xor<T>(self: Option<T>, optb: Option<T>): Option<T>
|
||||||
if self:isSome() and optb:isSome() then
|
if self:isSome() and optb:isSome() then
|
||||||
return None()
|
return None()
|
||||||
|
@ -193,11 +644,59 @@ function Option.xor<T>(self: Option<T>, optb: Option<T>): Option<T>
|
||||||
return None()
|
return None()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Inserts value into the option, then returns it.
|
||||||
|
|
||||||
|
If the option already contains a value, the old value is dropped.
|
||||||
|
|
||||||
|
See also [Option:getOrInsert], which doesn’t update the value if the
|
||||||
|
option already contains [Option:Some].
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local opt: Option<number> = None()
|
||||||
|
local val: number = opt:insert(1)
|
||||||
|
assert(val == 1)
|
||||||
|
assert(opt:unwrap() == 1)
|
||||||
|
|
||||||
|
local val: number = opt:insert(2)
|
||||||
|
assert(val == 2)
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param val T -- The value to insert
|
||||||
|
@return T
|
||||||
|
]=]
|
||||||
function Option.insert<T>(self: Option<T>, val: T): T
|
function Option.insert<T>(self: Option<T>, val: T): T
|
||||||
self._optValue = val
|
self._optValue = val
|
||||||
return self._optValue :: T
|
return self._optValue :: T
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Inserts value into the option, then returns it.
|
||||||
|
|
||||||
|
If the option already contains a value, the old value is dropped.
|
||||||
|
|
||||||
|
See also [Option:getOrInsert], which doesn’t update the value if the
|
||||||
|
option already contains [Option:Some].
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local opt: Option<number> = None()
|
||||||
|
local val: number = opt:insert(1)
|
||||||
|
assert(val == 1)
|
||||||
|
assert(opt:unwrap() == 1)
|
||||||
|
|
||||||
|
local val: number = opt:insert(2)
|
||||||
|
assert(val == 2)
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param val T -- The value to insert
|
||||||
|
@return T
|
||||||
|
]=]
|
||||||
function Option.getOrInsert<T>(self: Option<T>, val: T): T
|
function Option.getOrInsert<T>(self: Option<T>, val: T): T
|
||||||
if self:isNone() then
|
if self:isNone() then
|
||||||
self._optValue = val
|
self._optValue = val
|
||||||
|
@ -206,6 +705,26 @@ function Option.getOrInsert<T>(self: Option<T>, val: T): T
|
||||||
return self._optValue :: T
|
return self._optValue :: T
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Takes the value out of the option, leaving an [Option:None] in its place.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local x: Option<number> = Some(2)
|
||||||
|
local y: Option<number> = x.take()
|
||||||
|
assert(x == None())
|
||||||
|
assert(y == Some(2))
|
||||||
|
|
||||||
|
local x: Option<number> = None()
|
||||||
|
local y: Option<number> = x.take()
|
||||||
|
assert(x == None())
|
||||||
|
assert(y == None())
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@return Option<T>
|
||||||
|
]=]
|
||||||
function Option.take<T>(self: Option<T>): Option<T>
|
function Option.take<T>(self: Option<T>): Option<T>
|
||||||
if self:isSome() then
|
if self:isSome() then
|
||||||
local val = self._optValue :: T
|
local val = self._optValue :: T
|
||||||
|
@ -216,6 +735,29 @@ function Option.take<T>(self: Option<T>): Option<T>
|
||||||
return None()
|
return None()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Replaces the actual value in the option by the value given in parameter, returning
|
||||||
|
the old value if present, leaving an [Option:Some] in its place without
|
||||||
|
deinitializing either one.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local x: Option<number> = Some(2)
|
||||||
|
local old: Option<number> = x:replace(5)
|
||||||
|
assert(x == Some(5))
|
||||||
|
assert(old == Some(2))
|
||||||
|
|
||||||
|
local x: Option<number> = None()
|
||||||
|
local old: Option<number> = x:replace(3)
|
||||||
|
assert(x == Some(3))
|
||||||
|
assert(old == None())
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param val T
|
||||||
|
@return Option<T>
|
||||||
|
]=]
|
||||||
function Option.replace<T>(self: Option<T>, val: T): Option<T>
|
function Option.replace<T>(self: Option<T>, val: T): Option<T>
|
||||||
local current: Option<T> = self
|
local current: Option<T> = self
|
||||||
self._optValue = val
|
self._optValue = val
|
||||||
|
@ -227,6 +769,24 @@ function Option.replace<T>(self: Option<T>, val: T): Option<T>
|
||||||
return Some(current._optValue :: T)
|
return Some(current._optValue :: T)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Returns true if `val` is contained in the [Option:Some].
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local x: Option<number> = Some(2)
|
||||||
|
local y: Option<number> = None()
|
||||||
|
|
||||||
|
assert(x:contains(2))
|
||||||
|
assert(x:contains(4))
|
||||||
|
assert(not y:contains(2))
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param val T
|
||||||
|
@return boolean
|
||||||
|
]=]
|
||||||
function Option.contains<T>(self: Option<T>, val: T): boolean
|
function Option.contains<T>(self: Option<T>, val: T): boolean
|
||||||
if self:isSome() then
|
if self:isSome() then
|
||||||
return self._optValue == val
|
return self._optValue == val
|
||||||
|
@ -235,6 +795,27 @@ function Option.contains<T>(self: Option<T>, val: T): boolean
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Zips `self` with another [Option].
|
||||||
|
|
||||||
|
If `self` is [Option:Some](s) and other is [Option:Some](o), this method returns
|
||||||
|
[Option:Some]({s, o}). Otherwise, [Option:None] is returned.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local x: Option<number> = Some(1)
|
||||||
|
local y: Option<string> = Some("hi")
|
||||||
|
local z: Option<number> = None()
|
||||||
|
|
||||||
|
assert(x:zip(y) == Some({ 1, "hi" }))
|
||||||
|
assert(x:zip(z) == None())
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param other Option>U>
|
||||||
|
@return Option<{T | U}>
|
||||||
|
]=]
|
||||||
function Option.zip<T, U>(self: Option<T>, other: Option<U>): Option<{ T | U }>
|
function Option.zip<T, U>(self: Option<T>, other: Option<U>): Option<{ T | U }>
|
||||||
if self:isSome() and other:isSome() then
|
if self:isSome() and other:isSome() then
|
||||||
return Some({ self._optValue, other._optValue })
|
return Some({ self._optValue, other._optValue })
|
||||||
|
@ -243,6 +824,42 @@ function Option.zip<T, U>(self: Option<T>, other: Option<U>): Option<{ T | U }>
|
||||||
return None()
|
return None()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Zips `self` and another [Option] with function `op`.
|
||||||
|
|
||||||
|
If `self` is [Option:Some](s) and other is [Option:Some](o), this method returns
|
||||||
|
[Option:Some](op(s, o)). Otherwise, [Option:None] is returned.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
type Point = {
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
}
|
||||||
|
local Point: Point & {
|
||||||
|
new: (x: number, y: number) -> Point,
|
||||||
|
} = {}
|
||||||
|
|
||||||
|
function Point.new(x: number, y: number): Point
|
||||||
|
return {
|
||||||
|
x = x,
|
||||||
|
y = y,
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
local xCoord: Option<number> = Some(17.5)
|
||||||
|
local yCoord: Option<number> = Some(42.7)
|
||||||
|
|
||||||
|
assert(xCoord:zipWith(yCoord, Point.new), Some({ x = 17.5, y = 42.7 }))
|
||||||
|
assert(x:zipWith(None(), Point.new), None())
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@param other Option>U>
|
||||||
|
@param op (x: T, y: U) -> R?
|
||||||
|
@return Option<R>
|
||||||
|
]=]
|
||||||
function Option.zipWith<T, U, R>(self: Option<T>, other: Option<U>, op: (x: T, y: U) -> R?): Option<R>
|
function Option.zipWith<T, U, R>(self: Option<T>, other: Option<U>, op: (x: T, y: U) -> R?): Option<R>
|
||||||
if self:isSome() and other:isSome() then
|
if self:isSome() and other:isSome() then
|
||||||
local computed = op(self._optValue :: T, other._optValue :: U)
|
local computed = op(self._optValue :: T, other._optValue :: U)
|
||||||
|
@ -255,6 +872,25 @@ function Option.zipWith<T, U, R>(self: Option<T>, other: Option<U>, op: (x: T, y
|
||||||
return None()
|
return None()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Unzips an option containing a table of two options.
|
||||||
|
|
||||||
|
If `self` is `Some({a, b})` this method returns `(Some(a), Some(b))`.
|
||||||
|
Otherwise, `(None(), None())` is returned.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local x: Option<{ number | string }> = Some({ 1, "hi" })
|
||||||
|
local y: Option<{ number }> = None()
|
||||||
|
|
||||||
|
assert((x:unzip() == Some(1), Some("hi")))
|
||||||
|
assert((y:unzip() == None(), None()))
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@return (Option<A>, Option<B>)
|
||||||
|
]=]
|
||||||
function Option.unzip<T, A, B>(self: Option<T>): (Option<A>, Option<B>)
|
function Option.unzip<T, A, B>(self: Option<T>): (Option<A>, Option<B>)
|
||||||
if self:isSome() then
|
if self:isSome() then
|
||||||
if self:isSome() and typeof(self._optValue) == "table" and #self._optValue == 2 then
|
if self:isSome() and typeof(self._optValue) == "table" and #self._optValue == 2 then
|
||||||
|
@ -265,10 +901,47 @@ function Option.unzip<T, A, B>(self: Option<T>): (Option<A>, Option<B>)
|
||||||
return None(), None()
|
return None(), None()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Returns the inner value wrapped by the [Option].
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local x: Option<string> = Some("lol")
|
||||||
|
local y: Option<string> = None()
|
||||||
|
|
||||||
|
assert(x:getInner() == "lol")
|
||||||
|
assert(y:getInner() == nil)
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@return T?
|
||||||
|
]=]
|
||||||
function Option.getInner<T>(self: Option<T>): T?
|
function Option.getInner<T>(self: Option<T>): T?
|
||||||
return self._optValue
|
return self._optValue
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--[=[
|
||||||
|
@within Option
|
||||||
|
|
||||||
|
Returns a formatted representation of the option, often
|
||||||
|
used for printing to stdout.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
local x: Option<number> = Some(123)
|
||||||
|
local y: Option<number> = None()
|
||||||
|
|
||||||
|
print(x:display()) -- prints `Option::Some(123)`
|
||||||
|
print(y:display()) -- prints `Option::None`
|
||||||
|
```
|
||||||
|
|
||||||
|
@param self Option<T>
|
||||||
|
@return string
|
||||||
|
]=]
|
||||||
|
function Option.display<T>(self: Option<T>): string
|
||||||
|
return tostring(self)
|
||||||
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Option = Option,
|
Option = Option,
|
||||||
Some = Some,
|
Some = Some,
|
||||||
|
|
Loading…
Reference in a new issue