Chaining operator becomes ?. instead of .?

This commit is contained in:
Mark Spanbroek 2021-03-11 17:50:30 +01:00
parent 53924aeeda
commit 8683732d99
4 changed files with 59 additions and 57 deletions

View File

@ -65,27 +65,29 @@ else:
### 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:
> Note: before version 0.3.0, the operator was `.?` instead of `?.`
```nim
var numbers: ?seq[int]
var amount: ?int
numbers = @[1, 2, 3].some
amount = numbers.?len
amount = numbers?.len
# amount now holds the integer 3
numbers = seq[int].none
amount = numbers.?len
amount = numbers?.len
# amount now equals int.none
```
Invocations of the `.?` operator can be chained:
Invocations of the `?.` operator can be chained:
```nim
import sequtils
numbers = @[1, 1, 2, 2, 2].some
amount = numbers.?deduplicate.?len
amount = numbers?.deduplicate?.len
# amount now holds the integer 2
```
@ -147,7 +149,7 @@ proc fails: ?!seq[int] =
### Binding, chaining, fallbacks and operators
Binding with the `=?` operator, chaining with the `.?` operator, fallbacks with
Binding with the `=?` operator, chaining with the `?.` operator, fallbacks with
the `|?` operator, and all the other operators that work with Options also work
for Results:
```nim
@ -158,7 +160,7 @@ if x =? works():
# use x
# chaining:
let amount = works().?deduplicate.?len
let amount = works()?.deduplicate?.len
# fallback values:
let value = fails() |? @[]

View File

@ -1,33 +1,33 @@
import std/macros
template `.?`*(option: typed, identifier: untyped{nkIdent}): untyped =
template `?.`*(option: typed, identifier: untyped{nkIdent}): untyped =
option ->? option.unsafeGet.identifier
macro `.?`*(option: typed, infix: untyped{nkInfix}): untyped =
macro `?.`*(option: typed, infix: untyped{nkInfix}): untyped =
let left = infix[1]
infix[1] = quote do: `option`.?`left`
infix[1] = quote do: `option`?.`left`
infix
macro `.?`*(option: typed, bracket: untyped{nkBracketExpr}): untyped =
macro `?.`*(option: typed, bracket: untyped{nkBracketExpr}): untyped =
let left = bracket[0]
bracket[0] = quote do: `option`.?`left`
bracket[0] = quote do: `option`?.`left`
bracket
macro `.?`*(option: typed, dot: untyped{nkDotExpr}): untyped =
macro `?.`*(option: typed, dot: untyped{nkDotExpr}): untyped =
let left = dot[0]
dot[0] = quote do: `option`.?`left`
dot[0] = quote do: `option`?.`left`
dot
macro `.?`*(option: typed, call: untyped{nkCall}): untyped =
macro `?.`*(option: typed, call: untyped{nkCall}): untyped =
let procedure = call[0]
if call.len > 1:
if procedure.kind == nnkDotExpr:
let (inner, outer) = (procedure[0], procedure[1])
call[0] = outer
call.insert(1, quote do: `option`.?`inner`)
call.insert(1, quote do: `option`?.`inner`)
call
else:
call.insert(1, quote do: `option`.unsafeGet)
quote do: `option` ->? `call`
else:
quote do: `option`.?`procedure`
quote do: `option`?.`procedure`

View File

@ -10,28 +10,28 @@ suite "optionals":
check (?string is Option[string])
check (?seq[bool] is Option[seq[bool]])
test ".? can be used for chaining optionals":
test "?. can be used for chaining optionals":
let a: ?seq[int] = @[41, 42].some
let b: ?seq[int] = seq[int].none
check a.?len == 2.some
check b.?len == int.none
check a.?len.?uint8 == 2'u8.some
check b.?len.?uint8 == uint8.none
check a.?len() == 2.some
check b.?len() == int.none
check a.?distribute(2).?len() == 2.some
check b.?distribute(2).?len() == int.none
check a?.len == 2.some
check b?.len == int.none
check a?.len?.uint8 == 2'u8.some
check b?.len?.uint8 == uint8.none
check a?.len() == 2.some
check b?.len() == int.none
check a?.distribute(2)?.len() == 2.some
check b?.distribute(2)?.len() == int.none
test ".? chain can be followed by . calls and operators":
test "?. chain can be followed by . calls and operators":
let a = @[41, 42].some
check a.?len.get == 2
check a.?len.get.uint8.uint64 == 2'u64
check a.?len.get() == 2
check a.?len.get().uint8.uint64 == 2'u64
check a.?deduplicate()[0].?uint8.?uint64 == 41'u64.some
check a.?len + 1 == 3.some
check a.?deduplicate()[0] + 1 == 42.some
check a.?deduplicate.map(x => x) == @[41, 42].some
check a?.len.get == 2
check a?.len.get.uint8.uint64 == 2'u64
check a?.len.get() == 2
check a?.len.get().uint8.uint64 == 2'u64
check a?.deduplicate()[0]?.uint8?.uint64 == 41'u64.some
check a?.len + 1 == 3.some
check a?.deduplicate()[0] + 1 == 42.some
check a?.deduplicate.map(x => x) == @[41, 42].some
test "[] can be used for indexing optionals":
let a: ?seq[int] = @[1, 2, 3].some
@ -132,15 +132,15 @@ suite "optionals":
var amount: ?int
numbers = @[1, 2, 3].some
amount = numbers.?len
amount = numbers?.len
check amount == 3.some
numbers = seq[int].none
amount = numbers.?len
amount = numbers?.len
check amount == int.none
numbers = @[1, 1, 2, 2, 2].some
amount = numbers.?deduplicate.?len
amount = numbers?.deduplicate?.len
check amount == 2.some
# Fallback values

View File

@ -14,28 +14,28 @@ suite "result":
check (?!string is Result[string, ref CatchableError])
check (?!seq[bool] is Result[seq[bool], ref CatchableError])
test ".? can be used for chaining results":
test "?. can be used for chaining results":
let a: ?!seq[int] = @[41, 42].success
let b: ?!seq[int] = seq[int].failure error
check a.?len == 2.success
check b.?len == int.failure error
check a.?len.?uint8 == 2'u8.success
check b.?len.?uint8 == uint8.failure error
check a.?len() == 2.success
check b.?len() == int.failure error
check a.?distribute(2).?len() == 2.success
check b.?distribute(2).?len() == int.failure error
check a?.len == 2.success
check b?.len == int.failure error
check a?.len?.uint8 == 2'u8.success
check b?.len?.uint8 == uint8.failure error
check a?.len() == 2.success
check b?.len() == int.failure error
check a?.distribute(2)?.len() == 2.success
check b?.distribute(2)?.len() == int.failure error
test ".? chain can be followed by . calls and operators":
test "?. chain can be followed by . calls and operators":
let a = @[41, 42].success
check (a.?len.get == 2)
check (a.?len.get.uint8.uint64 == 2'u64)
check (a.?len.get() == 2)
check (a.?len.get().uint8.uint64 == 2'u64)
check (a.?deduplicate()[0].?uint8.?uint64 == 41'u64.success)
check (a.?len + 1 == 3.success)
check (a.?deduplicate()[0] + 1 == 42.success)
check (a.?deduplicate.map(x => x) == @[41, 42].success)
check (a?.len.get == 2)
check (a?.len.get.uint8.uint64 == 2'u64)
check (a?.len.get() == 2)
check (a?.len.get().uint8.uint64 == 2'u64)
check (a?.deduplicate()[0]?.uint8?.uint64 == 41'u64.success)
check (a?.len + 1 == 3.success)
check (a?.deduplicate()[0] + 1 == 42.success)
check (a?.deduplicate.map(x => x) == @[41, 42].success)
test "[] can be used for indexing optionals":
let a: ?!seq[int] = @[1, 2, 3].success
@ -130,7 +130,7 @@ suite "result":
fail
# chaining:
let amount = works().?deduplicate.?len
let amount = works()?.deduplicate?.len
check (amount == 2.success)
# fallback values: