mirror of
https://github.com/logos-storage/questionable.git
synced 2026-01-04 06:43:10 +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
|
# 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
|
### Option chaining
|
||||||
|
|
||||||
To safely access fields and call procs, you can use the `.?` operator:
|
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/options
|
||||||
import std/macros
|
import std/macros
|
||||||
|
import ./binding
|
||||||
import ./chaining
|
import ./chaining
|
||||||
import ./indexing
|
import ./indexing
|
||||||
import ./operators
|
import ./operators
|
||||||
@ -8,6 +9,7 @@ import ./without
|
|||||||
include ./errorban
|
include ./errorban
|
||||||
|
|
||||||
export options except get
|
export options except get
|
||||||
|
export binding
|
||||||
export chaining
|
export chaining
|
||||||
export indexing
|
export indexing
|
||||||
export without
|
export without
|
||||||
@ -42,20 +44,6 @@ template `->?`*[T,U,V](options: (?T, ?U), expression: ?V): ?V =
|
|||||||
else:
|
else:
|
||||||
V.none
|
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 =
|
template `|?`*[T](option: ?T, fallback: T): T =
|
||||||
if option.isSome:
|
if option.isSome:
|
||||||
option.unsafeGet()
|
option.unsafeGet()
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import std/macros
|
import std/macros
|
||||||
import ./resultsbase
|
import ./resultsbase
|
||||||
import ./options
|
import ./options
|
||||||
|
import ./binding
|
||||||
import ./chaining
|
import ./chaining
|
||||||
import ./indexing
|
import ./indexing
|
||||||
import ./operators
|
import ./operators
|
||||||
@ -9,6 +10,7 @@ import ./without
|
|||||||
include ./errorban
|
include ./errorban
|
||||||
|
|
||||||
export resultsbase except ok, err, isOk, isErr, get
|
export resultsbase except ok, err, isOk, isErr, get
|
||||||
|
export binding
|
||||||
export chaining
|
export chaining
|
||||||
export indexing
|
export indexing
|
||||||
export without
|
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 =
|
template `|?`*[T,E](value: Result[T,E], fallback: T): T =
|
||||||
value.valueOr(fallback)
|
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 =
|
proc option*[T,E](value: Result[T,E]): ?T =
|
||||||
if value.isOk:
|
if value.isOk:
|
||||||
value.unsafeGet.some
|
value.unsafeGet.some
|
||||||
|
|||||||
@ -126,10 +126,21 @@ suite "optionals":
|
|||||||
let b {.used.} = a
|
let b {.used.} = a
|
||||||
check count == 1
|
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 =
|
proc toString[T](option: ?T): string =
|
||||||
if value =? option:
|
if value =? option:
|
||||||
mixin value
|
|
||||||
$value
|
$value
|
||||||
else:
|
else:
|
||||||
"none"
|
"none"
|
||||||
@ -270,15 +281,6 @@ suite "optionals":
|
|||||||
someProc(int.none)
|
someProc(int.none)
|
||||||
someProc(42.some)
|
someProc(42.some)
|
||||||
|
|
||||||
# generics
|
|
||||||
|
|
||||||
proc genericProc[T](option: ?T) =
|
|
||||||
if value =? option:
|
|
||||||
mixin value
|
|
||||||
check value == 42
|
|
||||||
|
|
||||||
genericProc(42.some)
|
|
||||||
|
|
||||||
# Option chaining
|
# Option chaining
|
||||||
|
|
||||||
var numbers: ?seq[int]
|
var numbers: ?seq[int]
|
||||||
|
|||||||
@ -129,6 +129,28 @@ suite "result":
|
|||||||
let b {.used.} = a
|
let b {.used.} = a
|
||||||
check count == 1
|
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":
|
test "without statement works for results":
|
||||||
proc test1 =
|
proc test1 =
|
||||||
without a =? 42.success:
|
without a =? 42.success:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user