Fix use of =? in generic code

This commit is contained in:
Mark Spanbroek 2021-05-06 17:12:52 +02:00
parent 7303be50da
commit 2700038316
6 changed files with 64 additions and 55 deletions

View File

@ -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
View 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`)

View File

@ -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()

View File

@ -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

View File

@ -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]

View File

@ -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: