local option = require("option") local Option = option.Option type Option = option.Option local None = option.None local Some = option.Some local Result = {} export type Result = typeof(Result) & { _value: T, _error: E, typeId: "Result", } function Ok(val: T): Result return Result.new(val, (nil :: unknown) :: E) end function Err(err: E): Result return Result.new((nil :: unknown) :: T, err) end function Result.new(val: T, err: E) return setmetatable( { _value = val, _error = err, typeId = "Result", } :: Result, { __index = Result, __tostring = function(self: Result) if self:isOk() then return `{self.typeId}::Ok({self._value})` end if self:isErr() then return `{self.typeId}::Err({self._error})` end return `{self.typeId}` end, __eq = function(self: Result, other: Result) if typeof(self._value) ~= "table" and typeof(self._error) ~= "table" and typeof(other._value) ~= "table" and typeof(other._error) ~= "table" then return self._value == other._value and self._error == other._error end error(`eq: cannot compare {tostring(self)} and {tostring(other)}`) end, -- TODO: Implement __iter, once iterators traits exist } ) end function Result.isOk(self: Result): boolean if self._value == nil then return false end return true end function Result.isOkAnd(self: Result, predicate: (val: T?) -> boolean): boolean return self:isOk() and predicate(self._value) end function Result.isErr(self: Result) return not self:isOk() end function Result.isErrAnd(self: Result, predicate: (val: E) -> boolean): boolean if self:isErr() then return predicate(self._error) end return false end function Result.ok(self: Result): Option if self:isOk() then if self._value == nil then return None() end return Some(self._value) end return None() end function Result.err(self: Result): Option if self:isErr() then return Option.new(self._error) :: Option end return None() end function Result.map(self: Result, op: (val: T) -> U): Result if self:isOk() then return Result.new(op(self._value), self._error) :: Result end return Err(self._error) end function Result.mapOr(self: Result, default: U, op: (val: T) -> U): U if self:isOk() then return op(self._value) end return default end function Result.mapOrElse(self: Result, default: (val: E) -> U, op: (val: T) -> U): U if self:isOk() then return op(self._value) end return default(self._error) end function Result.mapErr(self: Result, op: (val: E) -> F): Result if self:isErr() then return Result.new(self._value, op(self._error)) end return Ok(self._value) end function Result.inspect(self: Result, op: (val: T) -> nil): Result if self:isOk() then op(self._value) end return self end function Result.inspectErr(self: Result, op: (val: E) -> nil): Result if self:isErr() then op(self._error) end return self end -- TODO: Iterator traits function Result.iter() end function Result.expect(self: Result, msg: string): T | never if self:isOk() then return self._value end return error(`panic: {msg}; {self._error}`) end function Result.unwrap(self: Result): T | never if self:isOk() then return self._value end return error(`panic: \`Result:unwrap()\` called on an \`Err\` value: {self._error}`) end function Result.unwrapOrDefault(self: Result): never return error("TODO: Result:unwrapOrDefault()") end function Result.exceptErr(self: Result, msg: string): E | never if self:isErr() then return self._error end return error(`panic: {msg}; {self._error}`) end function Result.unwrapErr(self: Result): E | never if self:isErr() then return self._error end return error(`panic: \`Result:unwrapErr()\` called on an \`Ok\` value: {self._value}`) end -- TODO: How the fuck do I implement this? function Result.intoOk(self: Result) end function Result.intoErr(self: Result) end function Result.and_(self: Result, res: Result): Result if self:isOk() then return res end return Err(self._error) end function Result.andThen(self: Result, op: (...any) -> any): Result if self:isOk() then return op(self._value) end return Err(self._error) :: Result end function Result.or_(self: Result, res: Result): Result | never if self:isErr() then return res end if self:isOk() then return Ok(self._value) :: Result end return error("called `Result:or()` with an invalid value") end function Result.orElse(self: Result, op: (x: E) -> Result): Result if self:isErr() then return op(self._error) end return Ok(self._value) end function Result.unwrapOr(self: Result, default: T): T if self:isOk() then return self._value end return default end function Result.unwrapOrElse(self: Result, op: (x: E) -> T) if self:isOk() then return self._value end return op(self._error) end function Result.contains(self: Result, val: T): boolean if self:isOk() then return self._value == val end return false end function Result.containsErr(self: Result, err: E): boolean if self:isErr() then return self._error == err end return false end function Result.transpose(self: Result, E>): Option> -- TODO: Instead of checking whether values are nil, use -- utility methods for Options once available if self._value == None() then return None() elseif self:isOkAnd(function(val): boolean return val._optValue == nil end) then return Some(Ok(self._value._optValue)) elseif self:isErr() then return Some(Err(self._error)) end error("`Result` is not transposable") end local x: Result, string> = Ok(None()) print(tostring(x)) return { Ok = Ok, Err = Err, Result = Result, } -- print(y:transpose()) -- this should have a typeerror, i need to fix this