123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- #
- #
- # Nim's Runtime Library
- # (c) Copyright 2015 Nim Contributors
- #
- # See the file "copying.txt", included in this
- # distribution, for details about the copyright.
- #
- ## 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)`).
- ##
- ## 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
- ## available, nor is it always a good solution.
- ##
- ##
- ## Basic usage
- ## ===========
- ##
- ## Let's start with an example: a procedure that finds the index of a character
- ## in a string.
- ##
- ## .. code-block:: nim
- ##
- ## import options
- ##
- ## proc find(haystack: string, needle: char): Option[int] =
- ## for i, c in haystack:
- ## if c == needle:
- ## return some(i)
- ## return none(int) # This line is actually optional,
- ## # because the default is empty
- ##
- ## .. code-block:: nim
- ##
- ## 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 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:
- ##
- ## .. code-block:: nim
- ##
- ## let result = "team".find('i')
- ##
- ## # Nothing was found, so the result is `none`.
- ## assert(result == none(int))
- ## # It has no value:
- ## assert(result.isNone)
- import typetraits
- type
- SomePointer = ref | ptr | pointer
- type
- Option*[T] = object
- ## An optional type that stores its value and state separately in a boolean.
- when T is SomePointer:
- val: T
- else:
- val: T
- has: bool
- 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 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(not val.isNil)
- result.val = val
- else:
- result.has = true
- result.val = val
- proc none*(T: typedesc): Option[T] =
- ## 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) 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:
- not self.val.isNil
- 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.isNil
- else:
- not self.has
- proc get*[T](self: Option[T]): T =
- ## 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 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 `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` 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` 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`.
- 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` 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 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 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 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
- result.add ")"
- 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
- # RefPerson is used to test that overloaded `==` operator is not called by
- # options. It is defined here in the global scope, because otherwise the test
- # will not even consider the `==` operator. Different bug?
- type RefPerson = ref object
- name: string
- proc `==`(a, b: RefPerson): bool =
- assert(not a.isNil and not b.isNil)
- a.name == b.name
- suite "options":
- # work around a bug in unittest
- let intNone = none(int)
- let stringNone = none(string)
- test "example":
- proc find(haystack: string, needle: char): Option[int] =
- for i, c in haystack:
- if c == needle:
- return some i
- check("abc".find('c').get() == 2)
- let result = "team".find('i')
- check result == intNone
- check result.isNone
- test "some":
- check some(6).get() == 6
- check some("a").unsafeGet() == "a"
- check some(6).isSome
- check some("a").isSome
- test "none":
- expect UnpackError:
- discard none(int).get()
- check(none(int).isNone)
- check(not none(string).isSome)
- test "equality":
- check some("a") == some("a")
- check some(7) != some(6)
- check some("a") != stringNone
- check intNone == intNone
- when compiles(some("a") == some(5)):
- check false
- when compiles(none(string) == none(int)):
- check false
- test "get with a default value":
- check(some("Correct").get("Wrong") == "Correct")
- check(stringNone.get("Correct") == "Correct")
- test "$":
- check($(some("Correct")) == "Some(\"Correct\")")
- check($(stringNone) == "None[string]")
- test "map with a void result":
- var procRan = 0
- some(123).map(proc (v: int) = procRan = v)
- check procRan == 123
- intNone.map(proc (v: int) = check false)
- test "map":
- check(some(123).map(proc (v: int): int = v * 2) == some(246))
- check(intNone.map(proc (v: int): int = v * 2).isNone)
- test "filter":
- check(some(123).filter(proc (v: int): bool = v == 123) == some(123))
- check(some(456).filter(proc (v: int): bool = v == 123).isNone)
- check(intNone.filter(proc (v: int): bool = check false).isNone)
- test "flatMap":
- proc addOneIfNotZero(v: int): Option[int] =
- if v != 0:
- result = some(v + 1)
- else:
- result = none(int)
- check(some(1).flatMap(addOneIfNotZero) == some(2))
- check(some(0).flatMap(addOneIfNotZero) == none(int))
- check(some(1).flatMap(addOneIfNotZero).flatMap(addOneIfNotZero) == some(3))
- proc maybeToString(v: int): Option[string] =
- if v != 0:
- result = some($v)
- else:
- result = none(string)
- check(some(1).flatMap(maybeToString) == some("1"))
- proc maybeExclaim(v: string): Option[string] =
- if v != "":
- result = some v & "!"
- else:
- result = none(string)
- check(some(1).flatMap(maybeToString).flatMap(maybeExclaim) == some("1!"))
- check(some(0).flatMap(maybeToString).flatMap(maybeExclaim) == none(string))
- test "SomePointer":
- var intref: ref int
- check(option(intref).isNone)
- intref.new
- check(option(intref).isSome)
- let tmp = option(intref)
- check(sizeof(tmp) == sizeof(ptr int))
- test "none[T]":
- check(none[int]().isNone)
- check(none(int) == none[int]())
- test "$ on typed with .name":
- type Named = object
- name: string
- let nobody = none(Named)
- check($nobody == "None[Named]")
- test "$ on type with name()":
- type Person = object
- myname: string
- let noperson = none(Person)
- check($noperson == "None[Person]")
- test "Ref type with overloaded `==`":
- let p = some(RefPerson.new())
- check p.isSome
|