Skip to content

logical operators for option type #8369

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 5 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 94 additions & 1 deletion lib/pure/options.nim
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,34 @@
## assert(false) # This will not be reached
## except UnpackError: # Because an exception is raised
## discard
##
## When you have a value of type ``Option[T]`` and you want to have
## it's value of type ``T``, you can access the value with ``get``.
## If you don't provide a fallback value there will be an exception.
## With the ``or`` operator you can chain together several expressions
## of type ``Option[T]`` and the whole expression will evaluate the
## operand from left to right until an the operand that is not
## none. This operand will be returned. If all operands are none, then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are not British, we use one space after a dot. (nitpick, I know)

## the expression evaluates to none. For convenience the last operand
## of such an expression does not need to be of type ``Option[T]``, it
## can be just of type ``T`` for the last fallback. If that is the
## case the whole expression will be of type ``T``, because it could
## not evaluate to none anymore.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your docs, while appreciated, are far too verbose.

Create a short, and to the point heading:

## Retrieving values from an option
## ========================
##
## Values can be retrieved in many ways, primarily using ``get`` or the ``or`` operator.
##
## Using ``get``
## --------------
##
## This is the simplest approach:
##
## ..code-block:: ....
##
##
## But it means there is an exception raised when the option isn't a ``some``.
##
## Using ``or``
## -------------
##
## ....

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Short sentences, and short specific examples are always better than a huge paragraph and a huge example.

##
## .. code-block:: nim
##
## var myOpt: Option[string]
## # Pick the first option that is not none.
## myOpt = some("abc") or some("def")
## assert myOpt == some("abc")
## var myStr: string
## # Pick the first option that is not none with final fallback.
## myStr = none(string) or some("abc") or "fallback"
## assert myStr == "abc"
## myStr = none(string) or none(string) or "fallback"
## assert myStr == "fallback"


import typetraits

type
Expand Down Expand Up @@ -139,6 +167,26 @@ proc get*[T](self: Option[T], otherwise: T): T =
else:
otherwise

template `or`*[T](x: Option[T]; y: T): T =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If an inline proc is enough, I much prefer them than templates

## When ``x`` is some return the content of ``x``. Otherwise
## evaluate ``y`` and return it. This operator can be used to
## convert a value of type ``Option[T]`` into a value of type ``T``
## by providing a fallback value in ``y``.
let xx = x
if xx.isSome:
xx.val
else:
y

template `or`*[T](x, y: Option[T]): Option[T] =
## When ``x`` is some, then return ``x``, else return
## ``y``. Evaluate ``y`` only when necessary.
let xx = x
if xx.isSome:
xx
else:
y

proc map*[T](self: Option[T], callback: proc (input: T)) =
## Applies a callback to the value in this Option
if self.isSome:
Expand All @@ -163,7 +211,7 @@ proc flatMap*[A, B](self: Option[A], callback: proc (input: A): Option[B]): Opti
## Applies a callback to the value in this Option and returns an
## option containing the new value. If this option is None, None will be
## returned. Similar to ``map``, with the difference that the callback
## returns an Option, not a raw value. This allows multiple procs with a
## returns an Option, not a raw value. This allows multiple procedures with a
## signature of ``A -> Option[B]`` (including A = B) to be chained together.
map(self, callback).flatten()

Expand Down Expand Up @@ -312,3 +360,48 @@ when isMainModule:

let noperson = none(Person)
check($noperson == "None[Person]")

test "logical operator()":
let strings = ["a", "b", "c", "d", "e"]
var sideEffects = 0

proc genSome(): Option[string] =
result = some(strings[sideEffects])
sideEffects += 1

proc genNone(): Option[string] =
result = none[string]()
sideEffects += 1

proc genStr(): string =
result = strings[sideEffects]
sideEffects += 1

proc reset(): void =
sideEffects = 0

# test ``or`` operator

check((genNone() or genNone() or genNone()) == none[string]())
check(sideEffects == 3)
reset()

check((genNone() or genSome()) == some("b"))
check(sideEffects == 2)
reset()

check((genNone() or genSome() or genSome()) == some("b"))
check(sideEffects == 2)
reset()

check((genNone() or genSome() or genStr()) == "b")
check(sideEffects == 2)
reset()

check((genNone() or genNone() or genStr()) == "c")
check(sideEffects == 3)
reset()

check((genSome() or genNone() or genSome() or "c") == "a")
check(sideEffects == 1)
reset()