2828# # option returns an option.
2929# #
3030# # Comparing
31- # # Comparing one option to another like ``some(100) == some(50)`` returns an
31+ # # Because of the way Nim treats comparators options uses special names to
32+ # # compare values. Every comparator is post-fixed with a question mark. So the
33+ # # equality operator becomes ``==?``, less than or equal to becomes ``<=?``
34+ # # and so on. This is also done to help alleviate some of the confusion.
35+ # # Comparing one option to another like ``some(100) ==? some(50)`` returns an
3236# # option. In this case they are not equal, so they return a ``none``. Note
3337# # however that it's not possible to compare two ``none`` values like this
34- # # ``none(int) == none(int)`` as it returns a ``none``. In the case that a
38+ # # ``none(int) ==? none(int)`` as it returns a ``none``. In the case that a
3539# # comparator passes, the *first* option is returned so
36- # # ``some(100) < some(200)`` returns ``some(100)``.
40+ # # ``some(100) <? some(200)`` returns ``some(100)``.
3741# #
3842# # Logic operations
3943# # Logic operations work on the options "has-ity" whether it has a value or
4953# # the one passed in. Since a ``none`` can't implicitly get a value ``not``
5054# # still returns a ``none`` when used on a ``none``.
5155# #
52- # # Booleans
53- # # Since both the comparators and the logic operators returns options it
54- # # might be confusing to look at something like:
55- # # ``if some(100) == some(100) == true`` but options will also implicitly
56- # # convert to a boolean when used in that context (with it's has-ity as the
57- # # value). So the above example could simply be written as
58- # # ``if some(100) == some(100)``. But any value can also implicitly be
59- # # converted to a ``some`` option. So we can simplify further with
60- # # ``if some(100) == 100``, this works as 100 is converted to a ``some``
61- # # which is then compared to the first ``some`` which returns a ``some(100)``
62- # # that automatically get's evaluated to true since it has a value. Should
63- # # the comparisson not hold we don't get an error but rather the comparator
64- # # would evaluate to a ``none[int]`` and all ``none`` options evaluates to
65- # # boolean false. Here we can see how treating the has-ity of an option makes
66- # # a lot of intuitive sense.
67- # #
6856# # Now that we have a better understanding of the nature of options we can take
6957# # a closer look at how to use them.
7058# #
157145# # This means that we can insert checks in a stream of options:
158146# #
159147# # .. code-block:: nim
160- # # assert (some(10) <= 20 ) == some(10)
161- # # assert (some(30) <= 20 ).isNone
162- # # assert (none(int) <= 20 ).isNone
148+ # # assert (some(10) <=? some(20) ) == some(10)
149+ # # assert (some(30) <=? some(20) ).isNone
150+ # # assert (none(int) <=? some(20) ).isNone
163151# # let x = some(10)
164- # # assert ((x >= 5) <= 20 ) == x
152+ # # assert ((x >=? some(5)) <=? some(20) ) == x
165153# # # This only returns some if x is in the range 5..20
166- # # # Note that since Nim currently rewrites >= to a <= statement with flipped
167- # # # arguments this currently doesn't work as it returns some(5).
168154# #
169155# # Options can also be used for conditional chaining. Let's use the ``find``
170156# # procedure we defined above:
186172# # also be used in if statements:
187173# #
188174# # .. code-block:: nim
189- # # if "hello world".find('w').?min(4) <= 2 :
175+ # # if ( "hello world".find('w').?min(4) <=? some(2)).isSome :
190176# # echo "Never run" # the first statement returns a none option, which when
191177# # # compared to 2 returns another none option. This none option is then
192- # # # evaluated to false. In this case min is still never run.
178+ # # # obviously not a some. Note that in this case it would be better to use
179+ # # # a `match`.
193180
194181import typetraits
195182import macros
@@ -267,10 +254,10 @@ proc get*[T](self: Option[T], otherwise: T): T =
267254 otherwise
268255
269256template either * (self, otherwise: untyped ): untyped =
270- # # Wrapper around get with a default, for use as ``either(x, 20)``, if ``x``
271- # # is a ``none `` then it will return ``20``, otherwise it will return the
272- # # value of ``x`` .
273- get ( self, otherwise)
257+ # # Similar in function to get, but if ``otherwise`` is a procedure it will not
258+ # # be evaluated if ``self `` is a ``some``. This means that ``otherwise`` can
259+ # # have side effects .
260+ if self.isSome: self.val else : otherwise
274261
275262proc map * [T](self: Option [T], callback: proc (input: T)) =
276263 # # Applies a callback to the value in this Option
@@ -487,27 +474,29 @@ proc optCmp*[T](self: Option[T], value: Option[T],
487474 if self.isSome and value.isSome and cmp (self.val, value.val):
488475 return self
489476
490- template `==` * [T](self: Option [T], value: Option [T]): Option [T] =
477+ proc `==` * [T](self: Option [T], value: Option [T]): bool {.error .} = discard
478+
479+ template `==?` * [T](self: Option [T], value: Option [T]): Option [T] =
491480 # # Wrapper for optCmp with ``==`` as the comparator
492481 optCmp (self, value, proc (val1, val2: T): bool = val1 == val2)
493482
494- template `not ` * [T](self: Option [T]): Option [T] =
495- # # Not always returns a ``none `` as a ``none`` can't implicitly get a value
496- none [T]( )
483+ template `!=? ` * [T](self: Option [T], value : Option [T]): Option [T] =
484+ # # Wrapper for optCmp with ``== `` as the comparator
485+ optCmp (self, value, proc (val1, val2: T): bool = val1 != val2 )
497486
498- template `<=` * [T](self: Option [T], value: Option [T]): Option [T] =
487+ template `<=? ` * [T](self: Option [T], value: Option [T]): Option [T] =
499488 # # Wrapper for optCmp with ``<=`` as the comparator
500489 optCmp (self, value, proc (val1, val2: T): bool = val1 <= val2)
501490
502- template `>=` * [T](self: Option [T], value: Option [T]): Option [T] =
491+ template `>=? ` * [T](self: Option [T], value: Option [T]): Option [T] =
503492 # # Wrapper for optCmp with ``>=`` as the comparator
504493 optCmp (self, value, proc (val1, val2: T): bool = val1 >= val2)
505494
506- template `>` * [T](self: Option [T], value: Option [T]): Option [T] =
495+ template `>? ` * [T](self: Option [T], value: Option [T]): Option [T] =
507496 # # Wrapper for optCmp with ``>`` as the comparator
508497 optCmp (self, value, proc (val1, val2: T): bool = val1 > val2)
509498
510- template `<` * [T](self: Option [T], value: Option [T]): Option [T] =
499+ template `<? ` * [T](self: Option [T], value: Option [T]): Option [T] =
511500 # # Wrapper for optCmp with ``<`` as the comparator
512501 optCmp (self, value, proc (val1, val2: T): bool = val1 < val2)
513502
@@ -521,6 +510,10 @@ proc toOpt*[T](value: T): Option[T] =
521510 # # it's not already an option.
522511 some (value)
523512
513+ template `not` * [T](self: Option [T]): Option [T] =
514+ # # Not always returns a ``none`` as a ``none`` can't implicitly get a value
515+ none [T]()
516+
524517template `or` * [T](x, y: Option [T]): Option [T] =
525518 # # When ``x`` is none, return ``y``
526519 let xx = x
@@ -536,14 +529,8 @@ template `and`*[T,U](x: Option[T]; y: Option[U]): Option[U] =
536529 else :
537530 none [U]()
538531
539- converter toSome * [T](value: T): Option [T] =
540- # # Automatic wrapping of values into ``some``
541- some (value)
542- converter toBool * [T](opt: Option [T]): bool =
543- # # Options use their has-ity as their boolean value
544- opt.isSome
545-
546532when isMainModule :
533+ echo some (10 ) < some (5 )
547534 import unittest, sequtils
548535
549536 suite " options" :
@@ -576,12 +563,12 @@ when isMainModule:
576563 check none (string ).isSome == false
577564
578565 test " equality" :
579- check some (" a" ) == some (" a" )
580- check some (7 ) != some (6 )
566+ check ( some (" a" ) == ? some (" a" )).isSome
567+ check ( some (7 ) != ? some (6 )).isSome
581568 check some (" a" ).isNone == false
582569 check intNone.isNone
583- check (intNone == intNone) == false
584- check (none (int ) == intNone) == false
570+ check (intNone == ? intNone).isNone
571+ check (none (int ) == ? intNone).isNone
585572
586573 when compiles (some (" a" ) == some (5 )):
587574 check false
@@ -591,7 +578,7 @@ when isMainModule:
591578 test " get with a default value" :
592579 check (some (" Correct" ).get (" Wrong" ) == " Correct" )
593580 check (stringNone.get (" Correct" ) == " Correct" )
594- check (either (some (100 ) < some (200 ), 10 ) == 100 )
581+ check (either (some (100 ) < ? some (200 ), 10 ) == 100 )
595582
596583 test " $" :
597584 check ($ (some (" Correct" )) == " Some(Correct)" )
@@ -604,13 +591,13 @@ when isMainModule:
604591 intNone.map (proc (v: int ) = check false )
605592
606593 test " map" :
607- check (some (123 ).map (proc (v: int ): int = v * 2 ) == some (246 ))
594+ check (some (123 ).map (proc (v: int ): int = v * 2 ) == ? some (246 )).isSome
608595 check (intNone.map (proc (v: int ): int = v * 2 ).isNone)
609596
610597 test " filter" :
611- check (some (123 ).filter (proc (v: int ): bool = v == 123 ) == some (123 ))
612- check ( some (456 ).filter (proc (v: int ): bool = v == 123 ).isNone)
613- check ( intNone.filter (proc (v: int ): bool = check false ).isNone)
598+ check (some (123 ).filter (proc (v: int ): bool = v == 123 ) == ? some (123 )).isSome
599+ check some (456 ).filter (proc (v: int ): bool = v == 123 ).isNone
600+ check intNone.filter (proc (v: int ): bool = check false ).isNone
614601
615602 test " flatMap" :
616603 proc addOneIfNotZero (v: int ): Option [int ] =
@@ -619,26 +606,26 @@ when isMainModule:
619606 else :
620607 result = none (int )
621608
622- check (some (1 ).flatMap (addOneIfNotZero) == some (2 ))
623- check ( some (0 ).flatMap (addOneIfNotZero).isNone)
624- check (some (1 ).flatMap (addOneIfNotZero).flatMap (addOneIfNotZero) ==
625- some (3 ))
609+ check (some (1 ).flatMap (addOneIfNotZero) == ? some (2 )).isSome
610+ check some (0 ).flatMap (addOneIfNotZero).isNone
611+ check (some (1 ).flatMap (addOneIfNotZero).flatMap (addOneIfNotZero) == ?
612+ some (3 )).isSome
626613
627614 proc maybeToString (v: int ): Option [string ] =
628615 if v != 0 :
629616 result = some ($ v)
630617 else :
631618 result = none (string )
632619
633- check (some (1 ).flatMap (maybeToString) == some (" 1" ))
620+ check (some (1 ).flatMap (maybeToString) == ? some (" 1" )).isSome
634621
635622 proc maybeExclaim (v: string ): Option [string ] =
636623 if v != " " :
637624 result = some v & " !"
638625 else :
639626 result = none (string )
640627
641- check (some (1 ).flatMap (maybeToString).flatMap (maybeExclaim) == some (" 1!" ))
628+ check (some (1 ).flatMap (maybeToString).flatMap (maybeExclaim) == ? some (" 1!" )).isSome
642629 check (some (0 ).flatMap (maybeToString).flatMap (maybeExclaim).isNone)
643630
644631 test " SomePointer" :
@@ -697,8 +684,8 @@ when isMainModule:
697684 test " compare" :
698685 proc leq (x, y: int ): bool =
699686 x <= y
700- check optCmp (10 , 20 , leq) == some (10 )
701- check optCmp (30 , 20 , leq).isNone
687+ check ( optCmp (some ( 10 ), some ( 20 ) , leq) == ? some (10 )).isSome
688+ check optCmp (some ( 30 ), some ( 20 ) , leq).isNone
702689
703690 test " logical operators" :
704691 let a = none [string ]()
@@ -724,29 +711,29 @@ when isMainModule:
724711 check (sideEffects == 3 )
725712 reset ()
726713
727- check ((genNone () or genSome ()) == some (" b" )) == true
714+ check ((genNone () or genSome ()) == ? some (" b" )).isSome
728715 check sideEffects == 2
729716 reset ()
730717
731- check (((genNone () or genSome ()) or genSome ()) == some (" b" )) == true
718+ check (((genNone () or genSome ()) or genSome ()) == ? some (" b" )).isSome
732719 check sideEffects == 2
733720 reset ()
734721
735- check ((genNone () or genSome ()) or " c" == " b" ) == true
722+ check ((genNone () or genSome ()) or some ( " c" )).unsafeGet == " b"
736723 check sideEffects == 2
737724 reset ()
738725
739- check ((genNone () or genNone ()) or " c" == " c" ) == true
726+ check ((genNone () or genNone ()) or some ( " c" )).unsafeGet == " c"
740727 check sideEffects == 2
741728 reset ()
742729
743- check (((genSome () or genNone ()) or genSome ()) or " c" == " a" ) == true
730+ check (((genSome () or genNone ()) or genSome ()) or some ( " c" )).unsafeGet == " a"
744731 check (sideEffects == 1 )
745732 reset ()
746733
747734 # test ``and`` operator
748735
749- check (((genSome () and genSome ()) and genSome ()) == some (" c" )) == true
736+ check (((genSome () and genSome ()) and genSome ()) == ? some (" c" )).isSome
750737 check sideEffects == 3
751738 reset ()
752739
@@ -758,34 +745,34 @@ when isMainModule:
758745 check sideEffects == 2
759746 reset ()
760747
761- check isNone ((genSome () and genNone ()) and " c" )
748+ check isNone ((genSome () and genNone ()) and some ( " c" ) )
762749 check sideEffects == 2
763750 reset ()
764751
765- check ((( genSome () and genSome ()) and " c " ) == some (" c" )) == true
752+ check ((genSome () and genSome ()) and some (" c" )).unsafeGet == " c "
766753 check sideEffects == 2
767754 reset ()
768755
769- check isNone (((genNone () and genSome ()) and genNone ()) and " c" )
756+ check isNone (((genNone () and genSome ()) and genNone ()) and some ( " c" ) )
770757 check sideEffects == 1
771758 reset ()
772759
773760 # change the type during the expression and mix ``and`` with ``or``
774- check genNone () and 1 or 2 == 2 == true
775- check genSome () and 1 or 2 == 1 == true
761+ check ( genNone () and some ( 1 ) or some ( 2 )).unsafeGet == 2
762+ check ( genSome () and some ( 1 ) or some ( 2 )).unsafeGet == 1
776763
777764 test " conditional continuation" :
778765 when not compiles (some (" Hello world" ).? find ('w' ).echo):
779766 check false
780- check (some (" Hello world" ).? find ('w' ) == 6 )
781- check (none (string ) or some (" hello corld" ) or some (" hello world" )).?
782- find ('w' ) == - 1
767+ check (some (" Hello world" ).? find ('w' ) == ? some ( 6 )).isSome
768+ check (( none (string ) or some (" hello corld" ) or some (" hello world" )).?
769+ find ('w' ) == ? some ( - 1 )).isSome
783770 var evaluated = false
784- if some (" team" ).? find ('i' ) == - 1 :
771+ if ( some (" team" ).? find ('i' ) == ? some ( - 1 )).isSome :
785772 evaluated = true
786773 check evaluated == true
787774 evaluated = false
788- if none (string ).? find ('i' ) == - 1 :
775+ if ( none (string ).? find ('i' ) == ? some ( - 1 )).isSome :
789776 evaluated = true
790777 check evaluated == false
791778
0 commit comments