diff options
-rw-r--r-- | lib/pure/optionals.nim | 216 |
1 files changed, 60 insertions, 156 deletions
diff --git a/lib/pure/optionals.nim b/lib/pure/optionals.nim index ef0b6e108..fb76c9037 100644 --- a/lib/pure/optionals.nim +++ b/lib/pure/optionals.nim @@ -14,11 +14,11 @@ ## ## This module implements types which encapsulate an optional value. ## -## A value of type ``?T`` (``Maybe[T]``) either contains a value `x` -## (represented as ``just(x)``) or is empty (``nothing(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 +## 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. ## ## @@ -32,28 +32,26 @@ ## ## import optionals ## -## proc find(haystack: string, needle: char): ?int = +## proc find(haystack: string, needle: char): Option[int] = ## for i, c in haystack: ## if c == needle: -## return just i -## return nothing(int) # This line is actually optional, -## # because the default is empty -## -## The ``?`` operator (template) is a shortcut for ``Maybe[T]``. +## return some(i) +## return none(int) # This line is actually optional, +## # because the default is empty ## ## .. code-block:: nim ## ## try: -## assert("abc".find('c')[] == 2) # Immediately extract the value +## assert("abc".find('c').get() == 2) # Immediately extract the value ## except FieldError: # If there is no value ## assert false # This will not be reached, because the value is present ## -## The ``[]`` operator demonstrated above returns the underlying value, or +## The ``get`` operation demonstrated above returns the underlying value, or ## raises ``FieldError`` if there is no value. There is another option for -## obtaining the value: ``val``, but you must only use it when you are -## absolutely sure the value is present (e.g. after checking ``has``). If you do -## not care about the tiny overhead that ``[]`` causes, you should simply never -## use ``val``. +## 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``. ## ## How to deal with an absence of a value: ## @@ -61,126 +59,62 @@ ## ## let result = "team".find('i') ## -## # Nothing was found, so the result is `nothing`. -## assert(result == nothing(int)) +## # Nothing was found, so the result is `none`. +## assert(result == none(int)) ## # It has no value: -## assert(result.has == false) -## # A different way to write it: -## assert(not result) +## assert(result.isNone) ## ## try: -## echo result[] +## echo result.get() ## assert(false) # This will not be reached ## except FieldError: # Because an exception is raised ## discard -## -## Now let's try out the extraction template. It returns whether a value -## is present and injects the value into a variable. It is meant to be used in -## a conditional. -## -## .. code-block:: nim -## -## if pos ?= "nim".find('i'): -## assert(pos is int) # This is a normal integer, no tricks. -## echo "Match found at position ", pos -## else: -## assert(false) # This will not be reached -## -## Or maybe you want to get the behavior of the standard library's ``find``, -## which returns `-1` if nothing was found. -## -## .. code-block:: nim -## -## assert(("team".find('i') or -1) == -1) -## assert(("nim".find('i') or -1) == 1) import typetraits type - Maybe*[T] = object + Option*[T] = object ## An optional type that stores its value and state separately in a boolean. val: T has: bool -template `?`*(T: typedesc): typedesc = - ## ``?T`` is equivalent to ``Maybe[T]``. - Maybe[T] - - -proc just*[T](val: T): Maybe[T] = - ## Returns a ``Maybe`` that has this value. +proc some*[T](val: T): Option[T] = + ## Returns a ``Option`` that has this value. result.has = true result.val = val -proc nothing*(T: typedesc): Maybe[T] = - ## Returns a ``Maybe`` for this type that has no value. +proc none*(T: typedesc): Option[T] = + ## Returns a ``Option`` for this type that has no value. result.has = false -proc has*(maybe: Maybe): bool = - ## Returns ``true`` if `maybe` isn't `nothing`. - maybe.has - -converter toBool*(maybe: Maybe): bool = - ## Same as ``has``. Allows to use a ``Maybe`` in boolean context. - maybe.has - +proc isSome*[T](self: Option[T]): bool = + self.has -proc unsafeVal*[T](maybe: Maybe[T]): T = - ## Returns the value of a `just`. Behavior is undefined for `nothing`. - assert maybe.has, "nothing has no val" - maybe.val +proc isNone*[T](self: Option[T]): bool = + not self.has -proc `[]`*[T](maybe: Maybe[T]): T = - ## Returns the value of `maybe`. Raises ``FieldError`` if it is `nothing`. - if not maybe: - raise newException(FieldError, "Can't obtain a value from a `nothing`") - maybe.val +proc unsafeGet*[T](self: Option[T]): T = + ## Returns the value of a `just`. Behavior is undefined for `none`. + assert self.isSome + self.val -template `or`*[T](maybe: Maybe[T], default: T): T = - ## Returns the value of `maybe`, or `default` if it is `nothing`. - if maybe: maybe.val - else: default +proc get*[T](self: Option[T]): T = + ## Returns contents of the Option. If it is none, then an exception is + ## thrown. + if self.isNone: + raise newException(FieldError, "Can't obtain a value from a `none`") + self.val -template `or`*[T](a, b: Maybe[T]): Maybe[T] = - ## Returns `a` if it is `just`, otherwise `b`. - if a: a - else: b -template `?=`*(into: expr, maybe: Maybe): bool = - ## Returns ``true`` if `maybe` isn't `nothing`. - ## - ## Injects a variable with the name specified by the argument `into` - ## with the value of `maybe`, or its type's default value if it is `nothing`. - ## - ## .. code-block:: nim - ## - ## proc message(): ?string = - ## just "Hello" - ## - ## if m ?= message(): - ## echo m - var into {.inject.}: type(maybe.val) - if maybe: - into = maybe.val - maybe - - -proc `==`*(a, b: Maybe): bool = - ## Returns ``true`` if both ``Maybe`` are `nothing`, +proc `==`*(a, b: Option): bool = + ## Returns ``true`` if both ``Option``s are `none`, ## or if they have equal values (a.has and b.has and a.val == b.val) or (not a.has and not b.has) -proc `$`[T](maybe: Maybe[T]): string = - ## Converts to string: `"just(value)"` or `"nothing(type)"` - if maybe.has: - "just(" & $maybe.val & ")" - else: - "nothing(" & T.name & ")" - when isMainModule: template expect(E: expr, body: stmt) = @@ -192,71 +126,41 @@ when isMainModule: block: # example - proc find(haystack: string, needle: char): ?int = + proc find(haystack: string, needle: char): Option[int] = for i, c in haystack: if c == needle: - return just i + return some i - assert("abc".find('c')[] == 2) + assert("abc".find('c').get() == 2) let result = "team".find('i') - assert result == nothing(int) + assert result == none(int) assert result.has == false - if pos ?= "nim".find('i'): - assert pos is int - assert pos == 1 - else: - assert false + block: # some + assert some(6).get() == 6 + assert some("a").unsafeGet() == "a" + assert some(6).isSome + assert some("a").isSome - assert(("team".find('i') or -1) == -1) - assert(("nim".find('i') or -1) == 1) - - block: # just - assert just(6)[] == 6 - assert just("a").unsafeVal == "a" - assert just(6).has - assert just("a") - - block: # nothing + block: # none expect FieldError: - discard nothing(int)[] - assert(not nothing(int).has) - assert(not nothing(string)) + discard none(int).get() + assert(none(int).isNone) + assert(not none(string).isSome) block: # equality - assert just("a") == just("a") - assert just(7) != just(6) - assert just("a") != nothing(string) - assert nothing(int) == nothing(int) + assert some("a") == some("a") + assert some(7) != some(6) + assert some("a") != none(string) + assert none(int) == none(int) - when compiles(just("a") == just(5)): + when compiles(some("a") == some(5)): assert false - when compiles(nothing(string) == nothing(int)): + when compiles(none(string) == none(int)): assert false block: # stringification - assert "just(7)" == $just(7) - assert "nothing(int)" == $nothing(int) - - block: # or - assert just(1) or just(2) == just(1) - assert nothing(string) or just("a") == just("a") - assert nothing(int) or nothing(int) == nothing(int) - assert just(5) or 2 == 2 - assert nothing(string) or "a" == "a" - - when compiles(just(1) or "2"): - assert false - when compiles(nothing(int) or just("a")): - assert false - - block: # extraction template - if a ?= just(5): - assert a == 5 - else: - assert false - - if b ?= nothing(string): - assert false + assert "some(7)" == $some(7) + assert "none(int)" == $none(int) |