You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Option and Result are two powerful abstractions that can be used
instead of raising errors. They can be a bit unwieldy though. This library is an
attempt at making their use a bit more elegant.
Installation
Use the Nimble package manager to add questionable to an existing
project. Add the following to its .nimble file:
requires "questionable >= 0.10.15 & < 0.11.0"
If you want to make use of Result types, then you also have to add either the
result package, or the stew package:
requires "results"# either this
requires "stew"# or this
Options
You can use ? to make a type optional. For example, the type ?int is just
short for Option[int].
import questionable
var x: ?int
Assigning values is done using the some and none procs from the standard library:
x =42.some # Option x now holds the value 42
x =int.none # Option x no longer holds a value
Option binding
The =? operator lets you bind the value inside an Option to a new variable. It
can be used inside of a conditional expression, for instance in an if
statement:
x =42.some
if y =? x:
# y equals 42 hereelse:
# this is never reached
x =int.none
if y =? x:
# this is never reachedelse:
# this is reached, and y is not defined
The without statement can be used to place guards that ensure that an Option
contains a value:
proc someProc(option: ?int) =
without value =? option:
# option did not contain a valuereturn# use value
Option chaining
To safely access fields and call procs, you can use the .? operator:
Note: in versions 0.3.x and 0.4.x, the operator was ?. instead of .?
var numbers: ?seq[int]
var amount: ?int
numbers =@[1, 2, 3].some
amount = numbers.?len
# amount now holds the integer 3
numbers =seq[int].none
amount = numbers.?len
# amount now equals int.none
Invocations of the .? operator can be chained:
import sequtils
numbers =@[1, 1, 2, 2, 2].some
amount = numbers.?deduplicate.?len
# amount now holds the integer 2
Fallback values
Use the |? operator to supply a fallback value when the Option does not hold
a value:
x =int.none
let z = x |?3# z equals 3
Obtaining value with !
The ! operator returns the value of an Option when you're absolutely sure that
it contains a value.
x =42.some
let dare =!x # dare equals 42
x =int.none
let crash =!x # raises a Defect
Operators
The operators [], -, +, @, *, /, div, mod, shl, shr, &,
<=, <, >=, > are all lifted, so they can be used directly on Options:
numbers =@[1, 2, 3].some
x =39.some
let indexed = numbers[0] # equals 1.somelet sum = x +3# equals 42.some
Results
Support for Result is considered experimental. If you want to use them you
have to explicitly import the questionable/results module:
import questionable/results
You can use ?! to make a Result type. These Result types either hold a value or
an error. For example the type ?!int is short for Result[int, ref CatchableError].
proc example: ?!int=# either return an integer or an error
Results can be made using the success and failure procs:
proc works: ?!seq[int] =# always returns a Result holding a sequence
success @[1, 1, 2, 2, 2]
proc fails: ?!seq[int] =# always returns a Result holding an error
failure "something went wrong"
Binding, chaining, fallbacks and operators
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:
import sequtils
# binding:if x =? works():
# use x# chaining:let amount = works().?deduplicate.?len
# fallback values:let value = fails() |?@[]
# lifted operators:let sum = works()[3] +40
Without statement
The without statement can also be used with Results. It provides access to any
errors that may arise:
proc someProc(r: ?!int) =
without value =? r, error:
# use `error` to get the error from rreturn# use value
Catching errors
When you want to use Results, but need to call a proc that may raise an
error, you can use catch:
import strutils
let x = parseInt("42").catch # equals 42.successlet y = parseInt("XX").catch # equals int.failure(..)