-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Optionals #2762
Merged
Merged
Optionals #2762
Changes from 5 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
32ebcfa
Implement optionals module
oprypin a66dcd9
Simplify optionals module
flaviut ae0c857
Simplify optionals tests
flaviut caa7301
Use custom exception for option unpack
flaviut d3ab60c
Remove Oleah Prypin as author
flaviut f9e95b2
Amend optionals docstring
flaviut File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
# | ||
# | ||
# Nim's Runtime Library | ||
# (c) Copyright 2015 Nim Contributors | ||
# | ||
# See the file "copying.txt", included in this | ||
# 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)``). | ||
## | ||
## 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. | ||
## | ||
## | ||
## Tutorial | ||
## ======== | ||
## | ||
## Let's start with an example: a procedure that finds the index of a character | ||
## in a string. | ||
## | ||
## .. code-block:: nim | ||
## | ||
## import optionals | ||
## | ||
## 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 | ||
## | ||
## try: | ||
## assert("abc".find('c').get() == 2) # Immediately extract the value | ||
## except UnpackError: # If there is no value | ||
## assert false # This will not be reached, because the value is present | ||
## | ||
## The ``get`` operation demonstrated above returns the underlying value, or | ||
## raises ``UnpackError`` if there is no value. 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``. | ||
## | ||
## 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) | ||
## | ||
## try: | ||
## echo result.get() | ||
## assert(false) # This will not be reached | ||
## except UnpackError: # Because an exception is raised | ||
## discard | ||
|
||
import typetraits | ||
|
||
|
||
type | ||
Option*[T] = object | ||
## An optional type that stores its value and state separately in a boolean. | ||
val: T | ||
has: bool | ||
UnpackError* = ref object of ValueError | ||
|
||
|
||
proc some*[T](val: T): Option[T] = | ||
## Returns a ``Option`` that has this value. | ||
result.has = true | ||
result.val = val | ||
|
||
proc none*(T: typedesc): Option[T] = | ||
## Returns a ``Option`` for this type that has no value. | ||
result.has = false | ||
|
||
|
||
proc isSome*[T](self: Option[T]): bool = | ||
self.has | ||
|
||
proc isNone*[T](self: Option[T]): bool = | ||
not self.has | ||
|
||
|
||
proc unsafeGet*[T](self: Option[T]): T = | ||
## Returns the value of a `just`. 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 | ||
## thrown. | ||
if self.isNone: | ||
raise UnpackError(msg : "Can't obtain a value from a `none`") | ||
self.val | ||
|
||
|
||
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) | ||
|
||
|
||
when isMainModule: | ||
import unittest | ||
|
||
suite "optionals": | ||
# 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 |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's a "just"? ;-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.