mirror of
https://github.com/logos-storage/questionable.git
synced 2026-01-02 13:53:11 +00:00
Fix use of =? in generic code
This commit is contained in:
parent
7303be50da
commit
2700038316
11
Readme.md
11
Readme.md
@ -75,17 +75,6 @@ proc someProc(option: ?int) =
|
||||
# use value
|
||||
```
|
||||
|
||||
When using `=?` in generic code you may face errors about undeclared
|
||||
identifiers. This is a limitation of Nim and can be worked around with a `mixin`
|
||||
statement:
|
||||
|
||||
```nim
|
||||
proc genericProc[T](option: ?T) =
|
||||
if value =? option:
|
||||
mixin value
|
||||
# use value
|
||||
```
|
||||
|
||||
### Option chaining
|
||||
|
||||
To safely access fields and call procs, you can use the `.?` operator:
|
||||
|
||||
25
questionable/binding.nim
Normal file
25
questionable/binding.nim
Normal file
@ -0,0 +1,25 @@
|
||||
import std/options
|
||||
import std/macros
|
||||
|
||||
proc option[T](option: Option[T]): Option[T] =
|
||||
option
|
||||
|
||||
template bindLet(name, expression): bool =
|
||||
let option = expression.option
|
||||
template name: auto {.used.} = option.unsafeGet()
|
||||
option.isSome
|
||||
|
||||
template bindVar(name, expression): bool =
|
||||
let option = expression.option
|
||||
var name : typeof(option.unsafeGet())
|
||||
if option.isSome:
|
||||
name = option.unsafeGet()
|
||||
option.isSome
|
||||
|
||||
macro `=?`*(name, expression): bool =
|
||||
name.expectKind({nnkIdent, nnkVarTy})
|
||||
if name.kind == nnkIdent:
|
||||
quote do: bindLet(`name`, `expression`)
|
||||
else:
|
||||
let name = name[0]
|
||||
quote do: bindVar(`name`, `expression`)
|
||||
@ -1,5 +1,6 @@
|
||||
import std/options
|
||||
import std/macros
|
||||
import ./binding
|
||||
import ./chaining
|
||||
import ./indexing
|
||||
import ./operators
|
||||
@ -8,6 +9,7 @@ import ./without
|
||||
include ./errorban
|
||||
|
||||
export options except get
|
||||
export binding
|
||||
export chaining
|
||||
export indexing
|
||||
export without
|
||||
@ -42,20 +44,6 @@ template `->?`*[T,U,V](options: (?T, ?U), expression: ?V): ?V =
|
||||
else:
|
||||
V.none
|
||||
|
||||
template `=?`*[T](name: untyped{nkIdent}, expression: ?T): bool =
|
||||
let option = expression
|
||||
template name: T {.used.} = option.unsafeGet()
|
||||
option.isSome
|
||||
|
||||
macro `=?`*[T](variable: untyped{nkVarTy}, expression: ?T): bool =
|
||||
let name = variable[0]
|
||||
quote do:
|
||||
let option = `expression`
|
||||
var `name` : typeof(option.unsafeGet())
|
||||
if option.isSome:
|
||||
`name` = option.unsafeGet()
|
||||
option.isSome
|
||||
|
||||
template `|?`*[T](option: ?T, fallback: T): T =
|
||||
if option.isSome:
|
||||
option.unsafeGet()
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import std/macros
|
||||
import ./resultsbase
|
||||
import ./options
|
||||
import ./binding
|
||||
import ./chaining
|
||||
import ./indexing
|
||||
import ./operators
|
||||
@ -9,6 +10,7 @@ import ./without
|
||||
include ./errorban
|
||||
|
||||
export resultsbase except ok, err, isOk, isErr, get
|
||||
export binding
|
||||
export chaining
|
||||
export indexing
|
||||
export without
|
||||
@ -76,25 +78,6 @@ template `->?`*[T,U,V](values: (?!T, ?!U), expression: ?!V): ?!V =
|
||||
template `|?`*[T,E](value: Result[T,E], fallback: T): T =
|
||||
value.valueOr(fallback)
|
||||
|
||||
macro `=?`*[T,E](name: untyped{nkIdent}, expression: Result[T,E]): bool =
|
||||
let unsafeGet = bindSym"unsafeGet"
|
||||
let isOk = bindSym"isOk"
|
||||
quote do:
|
||||
let value = `expression`
|
||||
template `name`: T {.used.} = value.`unsafeGet`()
|
||||
`isOk`(value)
|
||||
|
||||
macro `=?`*[T,E](variable: untyped{nkVarTy}, expression: Result[T,E]): bool =
|
||||
let name = variable[0]
|
||||
let unsafeGet = bindSym"unsafeGet"
|
||||
let isOk = bindSym"isOk"
|
||||
quote do:
|
||||
let value = `expression`
|
||||
var `name` : typeof(value.`unsafeGet`())
|
||||
if `isOk`(value):
|
||||
`name` = value.`unsafeGet`()
|
||||
`isOk`(value)
|
||||
|
||||
proc option*[T,E](value: Result[T,E]): ?T =
|
||||
if value.isOk:
|
||||
value.unsafeGet.some
|
||||
|
||||
@ -126,10 +126,21 @@ suite "optionals":
|
||||
let b {.used.} = a
|
||||
check count == 1
|
||||
|
||||
test "=? works in generic code with mixin statement":
|
||||
test "=? works in generic code":
|
||||
proc toString[T](option: ?T): string =
|
||||
if value =? option:
|
||||
$value
|
||||
else:
|
||||
"none"
|
||||
|
||||
check 42.some.toString == "42"
|
||||
check int.none.toString == "none"
|
||||
|
||||
test "=? works in generic code with variable hiding":
|
||||
let value {.used.} = "ignored"
|
||||
|
||||
proc toString[T](option: ?T): string =
|
||||
if value =? option:
|
||||
mixin value
|
||||
$value
|
||||
else:
|
||||
"none"
|
||||
@ -270,15 +281,6 @@ suite "optionals":
|
||||
someProc(int.none)
|
||||
someProc(42.some)
|
||||
|
||||
# generics
|
||||
|
||||
proc genericProc[T](option: ?T) =
|
||||
if value =? option:
|
||||
mixin value
|
||||
check value == 42
|
||||
|
||||
genericProc(42.some)
|
||||
|
||||
# Option chaining
|
||||
|
||||
var numbers: ?seq[int]
|
||||
|
||||
@ -129,6 +129,28 @@ suite "result":
|
||||
let b {.used.} = a
|
||||
check count == 1
|
||||
|
||||
test "=? works in generic code":
|
||||
proc toString[T](res: ?!T): string =
|
||||
if value =? res:
|
||||
$value
|
||||
else:
|
||||
"error"
|
||||
|
||||
check 42.success.toString == "42"
|
||||
check int.failure(error).toString == "error"
|
||||
|
||||
test "=? works in generic code with variable hiding":
|
||||
let value {.used.} = "ignored"
|
||||
|
||||
proc toString[T](res: ?!T): string =
|
||||
if value =? res:
|
||||
$value
|
||||
else:
|
||||
"error"
|
||||
|
||||
check 42.success.toString == "42"
|
||||
check int.failure(error).toString == "error"
|
||||
|
||||
test "without statement works for results":
|
||||
proc test1 =
|
||||
without a =? 42.success:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user