result: cleanups
* fix defect raising without `$` * doc updates * better `?` that doesn't cause raises effect (it should, because of FieldError but...)
This commit is contained in:
parent
5512e89d4c
commit
805ef4f1b2
|
@ -7,7 +7,7 @@ description = "Backports, standard library candidates and small utilities that
|
||||||
license = "Apache License 2.0"
|
license = "Apache License 2.0"
|
||||||
skipDirs = @["tests"]
|
skipDirs = @["tests"]
|
||||||
|
|
||||||
requires "nim >= 0.19.0"
|
requires "nim >= 1.2.0"
|
||||||
|
|
||||||
task test, "Run all tests":
|
task test, "Run all tests":
|
||||||
exec "nim c -r --threads:off tests/all_tests"
|
exec "nim c -r --threads:off tests/all_tests"
|
||||||
|
|
|
@ -215,18 +215,17 @@ type
|
||||||
## `f(x)` will call the slow `genericReset` and zero-init `x` again,
|
## `f(x)` will call the slow `genericReset` and zero-init `x` again,
|
||||||
## unnecessarily.
|
## unnecessarily.
|
||||||
##
|
##
|
||||||
## Comparing `Result` performance to exceptions in Nim is difficult - the
|
## Comparing `Result` performance to exceptions in Nim is difficult - it
|
||||||
## specific performance will depend on the error type used, the frequency
|
## will depend on the error type used, the frequency at which exceptions
|
||||||
## at which exceptions happen, the amount of error handling code in the
|
## happen, the amount of error handling code in the application and the
|
||||||
## application and the compiler and backend used.
|
## compiler and backend used.
|
||||||
##
|
##
|
||||||
## * the default C backend in nim uses `setjmp` for exception handling -
|
## * the default C backend in nim uses `setjmp` for exception handling -
|
||||||
## the relative performance of the happy path will depend on the structure
|
## the relative performance of the happy path will depend on the structure
|
||||||
## of the code: how many exception handlers there are, how much unwinding
|
## of the code: how many exception handlers there are, how much unwinding
|
||||||
## happens. `setjmp` works by taking a snapshot of the full CPU state and
|
## happens. `setjmp` works by taking a snapshot of the full CPU state and
|
||||||
## saving it in memory when enterting a try block (or an implict try
|
## saving it to memory when enterting a try block (or an implict try
|
||||||
## block, such as is introduced with `defer` and similar constructs) which
|
## block, such as is introduced with `defer` and similar constructs).
|
||||||
## is an expensive operation.
|
|
||||||
## * an efficient exception handling mechanism (like the C++ backend or
|
## * an efficient exception handling mechanism (like the C++ backend or
|
||||||
## `nlvm`) will usually have a lower cost on the happy path because the
|
## `nlvm`) will usually have a lower cost on the happy path because the
|
||||||
## value can be returned more efficiently. However, there is still a code
|
## value can be returned more efficiently. However, there is still a code
|
||||||
|
@ -236,9 +235,8 @@ type
|
||||||
## through a Result - at raise time, capturing a call stack and allocating
|
## through a Result - at raise time, capturing a call stack and allocating
|
||||||
## memory for the Exception is expensive, so the performance difference
|
## memory for the Exception is expensive, so the performance difference
|
||||||
## comes down to the complexity of the error type used.
|
## comes down to the complexity of the error type used.
|
||||||
## * checking for errors with Result is local branching operation that
|
## * checking for errors with Result is local branching operation that also
|
||||||
## happens on the happy path - even if all that is done is passing the
|
## happens on the happy path - this may be a cost.
|
||||||
## error to the next layer - when errors happen rarely, this may be a cost.
|
|
||||||
##
|
##
|
||||||
## An accurate summary might be that Exceptions are at its most efficient
|
## An accurate summary might be that Exceptions are at its most efficient
|
||||||
## when errors are not handled and don't happen.
|
## when errors are not handled and don't happen.
|
||||||
|
@ -273,7 +271,7 @@ func raiseResultError[T, E](self: Result[T, E]) {.noreturn, noinline.} =
|
||||||
raise (res ResultError[E])(msg: "Trying to access value with err", error: self.e)
|
raise (res ResultError[E])(msg: "Trying to access value with err", error: self.e)
|
||||||
|
|
||||||
func raiseResultDefect(m: string, v: auto) {.noreturn, noinline.} =
|
func raiseResultDefect(m: string, v: auto) {.noreturn, noinline.} =
|
||||||
if compiles($v): raise (ref ResultDefect)(msg: m & ": " & $v)
|
when compiles($v): raise (ref ResultDefect)(msg: m & ": " & $v)
|
||||||
else: raise (ref ResultDefect)(msg: m)
|
else: raise (ref ResultDefect)(msg: m)
|
||||||
|
|
||||||
template checkOk(self: Result) =
|
template checkOk(self: Result) =
|
||||||
|
@ -316,6 +314,11 @@ template isErr*(self: Result): bool = not self.o
|
||||||
func map*[T, E, A](
|
func map*[T, E, A](
|
||||||
self: Result[T, E], f: proc(x: T): A): Result[A, E] {.inline.} =
|
self: Result[T, E], f: proc(x: T): A): Result[A, E] {.inline.} =
|
||||||
## Transform value using f, or return error
|
## Transform value using f, or return error
|
||||||
|
##
|
||||||
|
## ```
|
||||||
|
## let r = Result[int, cstring).ok(42)
|
||||||
|
## assert r.map(proc (v: int): int = $v).get() == "42"
|
||||||
|
## ```
|
||||||
if self.isOk: result.ok(f(self.v))
|
if self.isOk: result.ok(f(self.v))
|
||||||
else: result.err(self.e)
|
else: result.err(self.e)
|
||||||
|
|
||||||
|
@ -570,6 +573,10 @@ template `?`*[T, E](self: Result[T, E]): T =
|
||||||
# TODO the v copy is here to prevent multiple evaluations of self - could
|
# TODO the v copy is here to prevent multiple evaluations of self - could
|
||||||
# probably avoid it with some fancy macro magic..
|
# probably avoid it with some fancy macro magic..
|
||||||
let v = (self)
|
let v = (self)
|
||||||
if v.isErr: return err(typeof(result), v.error)
|
if v.isErr:
|
||||||
|
when typeof(result) is typeof(v):
|
||||||
|
return v
|
||||||
|
else:
|
||||||
|
return err(typeof(result), v.e)
|
||||||
|
|
||||||
v.value
|
v.v
|
||||||
|
|
|
@ -173,8 +173,14 @@ func testQn2(): Result[int, string] =
|
||||||
# looks like we can even use it creatively like this
|
# looks like we can even use it creatively like this
|
||||||
if ?fails() == 42: raise (ref ValueError)(msg: "shouldn't happen")
|
if ?fails() == 42: raise (ref ValueError)(msg: "shouldn't happen")
|
||||||
|
|
||||||
|
func testQn3(): Result[bool, string] =
|
||||||
|
# different T but same E
|
||||||
|
let x = ?works() - ?works()
|
||||||
|
result.ok(x == 0)
|
||||||
|
|
||||||
doAssert testQn()[] == 0
|
doAssert testQn()[] == 0
|
||||||
doAssert testQn2().isErr
|
doAssert testQn2().isErr
|
||||||
|
doAssert testQn3()[]
|
||||||
|
|
||||||
type
|
type
|
||||||
AnEnum = enum
|
AnEnum = enum
|
||||||
|
|
Loading…
Reference in New Issue