diff options
author | Miran <narimiran@disroot.org> | 2019-02-22 12:28:00 +0100 |
---|---|---|
committer | Andreas Rumpf <rumpf_a@web.de> | 2019-02-22 12:28:00 +0100 |
commit | cedf7847518907b76933c053575afaa50944ad45 (patch) | |
tree | 28f2337febe476d8c787c4c1aab31701a93a6cca | |
parent | a4543c1aac22e6da8afc7b22445f0cd56380a6ad (diff) | |
download | Nim-cedf7847518907b76933c053575afaa50944ad45.tar.gz |
better docs: options (#10720)
-rw-r--r-- | lib/pure/options.nim | 280 |
1 files changed, 226 insertions, 54 deletions
diff --git a/lib/pure/options.nim b/lib/pure/options.nim index b827e1aa3..7a474e772 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -7,21 +7,18 @@ # distribution, for details about the copyright. # -## Abstract -## ======== -## ## This module implements types which encapsulate an optional value. ## -## A value of type ``Option[T]`` either contains a value `x` (represented as -## ``some(x)``) or is empty (``none(T)``). +## A value of type `Option[T]` either contains a value `x` (represented as +## `some(x)`) or is empty (`none(T)`). ## ## This can be useful when you have a value that can be present or not. The -## absence of a value is often represented by ``nil``, but it is not always +## absence of a value is often represented by `nil`, but it is not always ## available, nor is it always a good solution. ## ## -## Tutorial -## ======== +## Basic usage +## =========== ## ## Let's start with an example: a procedure that finds the index of a character ## in a string. @@ -41,16 +38,12 @@ ## ## let found = "abc".find('c') ## assert found.isSome and found.get() == 2 -## -## The ``get`` operation demonstrated above returns the underlying value, or -## raises ``UnpackError`` if there is no value. Note that ``UnpackError`` inherits -## from ``system.Defect``, and should therefore never be catched. Instead, rely on -## checking if the option contains a value with ``isSome`` and ``isNone``. ## -## There is another option for obtaining the value: ``unsafeGet``, but you must -## only use it when you are absolutely sure the value is present (e.g. after -## checking ``isSome``). If you do not care about the tiny overhead that ``get`` -## causes, you should simply never use ``unsafeGet``. +## The `get` operation demonstrated above returns the underlying value, or +## raises `UnpackError` if there is no value. Note that `UnpackError` +## inherits from `system.Defect`, and should therefore never be caught. +## Instead, rely on checking if the option contains a value with +## `isSome <#isSome,Option[T]>`_ and `isNone <#isNone,Option[T]>`_ procs. ## ## How to deal with an absence of a value: ## @@ -79,8 +72,44 @@ type UnpackError* = object of Defect + +proc option*[T](val: T): Option[T] = + ## Can be used to convert a pointer type (`ptr` or `ref`) to an option type. + ## It converts `nil` to `None`. + ## + ## See also: + ## * `some <#some,T>`_ + ## * `none <#none,typedesc>`_ + runnableExamples: + type + Foo = ref object + a: int + b: string + var c: Foo + assert c.isNil + var d = option(c) + assert d.isNone + + result.val = val + when T isnot SomePointer: + result.has = true + proc some*[T](val: T): Option[T] = - ## Returns a ``Option`` that has this value. + ## Returns an `Option` that has the value `val`. + ## + ## See also: + ## * `option <#option,T>`_ + ## * `none <#none,typedesc>`_ + ## * `isSome <#isSome,Option[T]>`_ + runnableExamples: + var + a = some("abc") + b = some(42) + assert $type(a) == "Option[system.string]" + assert b.isSome + assert a.get == "abc" + assert $b == "Some(42)" + when T is SomePointer: assert val != nil result.val = val @@ -88,107 +117,240 @@ proc some*[T](val: T): Option[T] = result.has = true result.val = val -proc option*[T](val: T): Option[T] = - ## Can be used to convert a pointer type to an option type. It - ## converts ``nil`` to the none-option. - result.val = val - when T isnot SomePointer: - result.has = true - proc none*(T: typedesc): Option[T] = - ## Returns an ``Option`` for this type that has no value. + ## Returns an `Option` for this type that has no value. + ## + ## See also: + ## * `option <#option,T>`_ + ## * `some <#some,T>`_ + ## * `isNone <#isNone,Option[T]>`_ + runnableExamples: + var a = none(int) + assert a.isNone + assert $type(a) == "Option[system.int]" + # the default is the none type discard proc none*[T]: Option[T] = - ## Alias for ``none(T)``. + ## Alias for `none(T) proc <#none,typedesc>`_. none(T) proc isSome*[T](self: Option[T]): bool {.inline.} = + ## Checks if an `Option` contains a value. + runnableExamples: + var + a = some(42) + b = none(string) + assert a.isSome + assert not b.isSome + when T is SomePointer: self.val != nil else: self.has proc isNone*[T](self: Option[T]): bool {.inline.} = + ## Checks if an `Option` is empty. + runnableExamples: + var + a = some(42) + b = none(string) + assert not a.isNone + assert b.isNone when T is SomePointer: self.val == nil else: not self.has -proc unsafeGet*[T](self: Option[T]): T = - ## Returns the value of a ``some``. Behavior is undefined for ``none``. - assert self.isSome - self.val - proc get*[T](self: Option[T]): T = - ## Returns contents of the Option. If it is none, then an exception is + ## Returns contents of an `Option`. If it is `None`, then an exception is ## thrown. + ## + ## See also: + ## * `get proc <#get,Option[T],T>`_ with the default return value + runnableExamples: + let + a = some(42) + b = none(string) + assert a.get == 42 + doAssertRaises(UnpackError): + echo b.get + if self.isNone: raise newException(UnpackError, "Can't obtain a value from a `none`") self.val proc get*[T](self: Option[T], otherwise: T): T = - ## Returns the contents of this option or `otherwise` if the option is none. + ## Returns the contents of the `Option` or an `otherwise` value if + ## the `Option` is `None`. + runnableExamples: + var + a = some(42) + b = none(int) + assert a.get(9999) == 42 + assert b.get(9999) == 9999 + if self.isSome: self.val else: otherwise proc get*[T](self: var Option[T]): var T = - ## Returns contents of the Option. If it is none, then an exception is - ## thrown. + ## Returns contents of the `var Option`. If it is `None`, then an exception + ## is thrown. + runnableExamples: + let + a = some(42) + b = none(string) + assert a.get == 42 + doAssertRaises(UnpackError): + echo b.get + if self.isNone: raise newException(UnpackError, "Can't obtain a value from a `none`") return self.val proc map*[T](self: Option[T], callback: proc (input: T)) = - ## Applies a callback to the value in this Option + ## Applies a `callback` function to the value of the `Option`, if it has one. + ## + ## See also: + ## * `map proc <#map,Option[T],proc(T)_2>`_ for a version with a callback + ## which returns a value + ## * `filter proc <#filter,Option[T],proc(T)>`_ + runnableExamples: + var d = 0 + proc saveDouble(x: int) = + d = 2*x + + let + a = some(42) + b = none(int) + + b.map(saveDouble) + assert d == 0 + a.map(saveDouble) + assert d == 84 + if self.isSome: callback(self.val) proc map*[T, R](self: Option[T], callback: proc (input: T): R): Option[R] = - ## 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 + ## Applies a `callback` function to the value of the `Option` and returns an + ## `Option` containing the new value. + ## + ## If the `Option` is `None`, `None` of the return type of the `callback` + ## will be returned. + ## + ## See also: + ## * `flatMap proc <#flatMap,Option[A],proc(A)>`_ for a version with a + ## callback which returns an `Option` + ## * `filter proc <#filter,Option[T],proc(T)>`_ + runnableExamples: + var + a = some(42) + b = none(int) + + proc isEven(x: int): bool = + x mod 2 == 0 + + assert $(a.map(isEven)) == "Some(true)" + assert $(b.map(isEven)) == "None[bool]" + if self.isSome: some[R]( callback(self.val) ) else: none(R) proc flatten*[A](self: Option[Option[A]]): Option[A] = - ## Remove one level of structure in a nested Option. + ## Remove one level of structure in a nested `Option`. + runnableExamples: + let a = some(some(42)) + assert $flatten(a) == "Some(42)" + if self.isSome: self.val else: none(A) proc flatMap*[A, B](self: Option[A], callback: proc (input: A): Option[B]): Option[B] = - ## 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 - ## signature of ``A -> Option[B]`` (including A = B) to be chained together. + ## Applies a `callback` function to the value of the `Option` and returns an + ## `Option` containing the new value. + ## + ## If the `Option` is `None`, `None` of the return type of the `callback` + ## 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 + ## signature of `A -> Option[B]` to be chained together. + ## + ## See also: + ## * `flatten proc <#flatten,Option[Option[A]]>`_ + ## * `filter proc <#filter,Option[T],proc(T)>`_ + runnableExamples: + proc doublePositives(x: int): Option[int] = + if x > 0: + return some(2*x) + else: + return none(int) + let + a = some(42) + b = none(int) + c = some(-11) + assert a.flatMap(doublePositives) == some(84) + assert b.flatMap(doublePositives) == none(int) + assert c.flatMap(doublePositives) == none(int) + map(self, callback).flatten() proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] = - ## Applies a callback to the value in this Option. If the callback returns - ## `true`, the option is returned as a Some. If it returns false, it is - ## returned as a None. + ## Applies a `callback` to the value of the `Option`. + ## + ## If the `callback` returns `true`, the option is returned as `Some`. + ## If it returns `false`, it is returned as `None`. + ## + ## See also: + ## * `map proc <#map,Option[T],proc(T)_2>`_ + ## * `flatMap proc <#flatMap,Option[A],proc(A)>`_ + runnableExamples: + proc isEven(x: int): bool = + x mod 2 == 0 + let + a = some(42) + b = none(int) + c = some(-11) + assert a.filter(isEven) == some(42) + assert b.filter(isEven) == none(int) + assert c.filter(isEven) == none(int) + if self.isSome and not callback(self.val): none(T) else: self proc `==`*(a, b: Option): bool = - ## Returns ``true`` if both ``Option``s are ``none``, - ## or if they have equal values + ## Returns `true` if both `Option`s are `None`, + ## or if they are both `Some` and have equal values. + runnableExamples: + let + a = some(42) + b = none(int) + c = some(42) + d = none(int) + + assert a == c + assert b == d + assert not (a == b) + (a.isSome and b.isSome and a.val == b.val) or (not a.isSome and not b.isSome) proc `$`*[T](self: Option[T]): string = - ## Get the string representation of this option. If the option has a value, - ## the result will be `Some(x)` where `x` is the string representation of the contained value. - ## If the option does not have a value, the result will be `None[T]` where `T` is the name of - ## the type contained in the option. + ## Get the string representation of the `Option`. + ## + ## If the `Option` has a value, the result will be `Some(x)` where `x` + ## is the string representation of the contained value. + ## If the `Option` does not have a value, the result will be `None[T]` + ## where `T` is the name of the type contained in the `Option`. if self.isSome: result = "Some(" result.addQuoted self.val @@ -196,6 +358,16 @@ proc `$`*[T](self: Option[T]): string = else: result = "None[" & name(T) & "]" +proc unsafeGet*[T](self: Option[T]): T = + ## Returns the value of a `some`. Behavior is undefined for `none`. + ## + ## **Note:** Use it only when you are **absolutely sure** the value is present + ## (e.g. after checking `isSome <#isSome,Option[T]>`_). + ## Generally, using `get proc <#get,Option[T]>`_ is preferred. + assert self.isSome + self.val + + when isMainModule: import unittest, sequtils |