summary refs log tree commit diff stats
path: root/lib/pure
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure')
-rw-r--r--lib/pure/options.nim280
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