28
28
# # option returns an option.
29
29
# #
30
30
# # 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
32
36
# # option. In this case they are not equal, so they return a ``none``. Note
33
37
# # 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
35
39
# # comparator passes, the *first* option is returned so
36
- # # ``some(100) < some(200)`` returns ``some(100)``.
40
+ # # ``some(100) <? some(200)`` returns ``some(100)``.
37
41
# #
38
42
# # Logic operations
39
43
# # Logic operations work on the options "has-ity" whether it has a value or
49
53
# # the one passed in. Since a ``none`` can't implicitly get a value ``not``
50
54
# # still returns a ``none`` when used on a ``none``.
51
55
# #
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
- # #
68
56
# # Now that we have a better understanding of the nature of options we can take
69
57
# # a closer look at how to use them.
70
58
# #
157
145
# # This means that we can insert checks in a stream of options:
158
146
# #
159
147
# # .. 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
163
151
# # let x = some(10)
164
- # # assert ((x >= 5) <= 20 ) == x
152
+ # # assert ((x >=? some(5)) <=? some(20) ) == x
165
153
# # # 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).
168
154
# #
169
155
# # Options can also be used for conditional chaining. Let's use the ``find``
170
156
# # procedure we defined above:
186
172
# # also be used in if statements:
187
173
# #
188
174
# # .. code-block:: nim
189
- # # if "hello world".find('w').?min(4) <= 2 :
175
+ # # if ( "hello world".find('w').?min(4) <=? some(2)).isSome :
190
176
# # echo "Never run" # the first statement returns a none option, which when
191
177
# # # 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`.
193
180
194
181
import typetraits
195
182
import macros
@@ -267,10 +254,10 @@ proc get*[T](self: Option[T], otherwise: T): T =
267
254
otherwise
268
255
269
256
template 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
274
261
275
262
proc map*[T](self: Option[T], callback: proc (input: T)) =
276
263
## Applies a callback to the value in this Option
@@ -487,27 +474,29 @@ proc optCmp*[T](self: Option[T], value: Option[T],
487
474
if self.isSome and value.isSome and cmp(self.val, value.val):
488
475
return self
489
476
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] =
491
480
## Wrapper for optCmp with ``==`` as the comparator
492
481
optCmp(self, value, proc (val1, val2: T): bool = val1 == val2)
493
482
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 )
497
486
498
- template `<=`*[T](self: Option[T], value: Option[T]): Option[T] =
487
+ template `<=? `*[T](self: Option[T], value: Option[T]): Option[T] =
499
488
## Wrapper for optCmp with ``<=`` as the comparator
500
489
optCmp(self, value, proc (val1, val2: T): bool = val1 <= val2)
501
490
502
- template `>=`*[T](self: Option[T], value: Option[T]): Option[T] =
491
+ template `>=? `*[T](self: Option[T], value: Option[T]): Option[T] =
503
492
## Wrapper for optCmp with ``>=`` as the comparator
504
493
optCmp(self, value, proc (val1, val2: T): bool = val1 >= val2)
505
494
506
- template `>`*[T](self: Option[T], value: Option[T]): Option[T] =
495
+ template `>? `*[T](self: Option[T], value: Option[T]): Option[T] =
507
496
## Wrapper for optCmp with ``>`` as the comparator
508
497
optCmp(self, value, proc (val1, val2: T): bool = val1 > val2)
509
498
510
- template `<`*[T](self: Option[T], value: Option[T]): Option[T] =
499
+ template `<? `*[T](self: Option[T], value: Option[T]): Option[T] =
511
500
## Wrapper for optCmp with ``<`` as the comparator
512
501
optCmp(self, value, proc (val1, val2: T): bool = val1 < val2)
513
502
@@ -521,6 +510,10 @@ proc toOpt*[T](value: T): Option[T] =
521
510
## it's not already an option.
522
511
some(value)
523
512
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
+
524
517
template `or`*[T](x, y: Option[T]): Option[T] =
525
518
## When ``x`` is none, return ``y``
526
519
let xx = x
@@ -536,14 +529,8 @@ template `and`*[T,U](x: Option[T]; y: Option[U]): Option[U] =
536
529
else:
537
530
none[U]()
538
531
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
-
546
532
when isMainModule :
533
+ echo some(10) < some(5)
547
534
import unittest, sequtils
548
535
549
536
suite "options":
@@ -576,12 +563,12 @@ when isMainModule:
576
563
check none(string ).isSome == false
577
564
578
565
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
581
568
check some("a").isNone == false
582
569
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
585
572
586
573
when compiles(some("a") == some(5)):
587
574
check false
@@ -591,7 +578,7 @@ when isMainModule:
591
578
test "get with a default value":
592
579
check(some("Correct").get("Wrong") == "Correct")
593
580
check(stringNone.get("Correct") == "Correct")
594
- check(either(some(100) < some(200), 10) == 100)
581
+ check(either(some(100) <? some(200), 10) == 100)
595
582
596
583
test "$":
597
584
check($(some("Correct")) == "Some(Correct)")
@@ -604,13 +591,13 @@ when isMainModule:
604
591
intNone.map(proc (v: int ) = check false)
605
592
606
593
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
608
595
check(intNone.map(proc (v: int ): int = v * 2).isNone)
609
596
610
597
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
614
601
615
602
test "flatMap":
616
603
proc addOneIfNotZero(v: int ): Option[int ] =
@@ -619,26 +606,26 @@ when isMainModule:
619
606
else:
620
607
result = none(int )
621
608
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
626
613
627
614
proc maybeToString(v: int ): Option[string ] =
628
615
if v != 0:
629
616
result = some($v)
630
617
else:
631
618
result = none(string )
632
619
633
- check(some(1).flatMap(maybeToString) == some("1"))
620
+ check (some(1).flatMap(maybeToString) ==? some("1")).isSome
634
621
635
622
proc maybeExclaim(v: string ): Option[string ] =
636
623
if v != "":
637
624
result = some v & "!"
638
625
else:
639
626
result = none(string )
640
627
641
- check(some(1).flatMap(maybeToString).flatMap(maybeExclaim) == some("1!"))
628
+ check (some(1).flatMap(maybeToString).flatMap(maybeExclaim) ==? some("1!")).isSome
642
629
check(some(0).flatMap(maybeToString).flatMap(maybeExclaim).isNone)
643
630
644
631
test "SomePointer":
@@ -697,8 +684,8 @@ when isMainModule:
697
684
test " compare" :
698
685
proc leq(x, y: int ): bool =
699
686
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
702
689
703
690
test " logical operators" :
704
691
let a = none[string ]()
@@ -724,29 +711,29 @@ when isMainModule:
724
711
check(sideEffects == 3 )
725
712
reset()
726
713
727
- check ((genNone() or genSome()) == some(" b" )) == true
714
+ check ((genNone() or genSome()) == ? some(" b" )).isSome
728
715
check sideEffects == 2
729
716
reset()
730
717
731
- check (((genNone() or genSome()) or genSome()) == some(" b" )) == true
718
+ check (((genNone() or genSome()) or genSome()) == ? some(" b" )).isSome
732
719
check sideEffects == 2
733
720
reset()
734
721
735
- check ((genNone() or genSome()) or " c" == " b" ) == true
722
+ check ((genNone() or genSome()) or some( " c" )).unsafeGet == " b"
736
723
check sideEffects == 2
737
724
reset()
738
725
739
- check ((genNone() or genNone()) or " c" == " c" ) == true
726
+ check ((genNone() or genNone()) or some( " c" )).unsafeGet == " c"
740
727
check sideEffects == 2
741
728
reset()
742
729
743
- check (((genSome() or genNone()) or genSome()) or " c" == " a" ) == true
730
+ check (((genSome() or genNone()) or genSome()) or some( " c" )).unsafeGet == " a"
744
731
check(sideEffects == 1 )
745
732
reset()
746
733
747
734
# test ``and`` operator
748
735
749
- check (((genSome() and genSome()) and genSome()) == some(" c" )) == true
736
+ check (((genSome() and genSome()) and genSome()) == ? some(" c" )).isSome
750
737
check sideEffects == 3
751
738
reset()
752
739
@@ -758,34 +745,34 @@ when isMainModule:
758
745
check sideEffects == 2
759
746
reset()
760
747
761
- check isNone((genSome() and genNone()) and " c" )
748
+ check isNone((genSome() and genNone()) and some( " c" ) )
762
749
check sideEffects == 2
763
750
reset()
764
751
765
- check ((( genSome() and genSome()) and " c " ) == some(" c" )) == true
752
+ check ((genSome() and genSome()) and some(" c" )).unsafeGet == " c "
766
753
check sideEffects == 2
767
754
reset()
768
755
769
- check isNone(((genNone() and genSome()) and genNone()) and " c" )
756
+ check isNone(((genNone() and genSome()) and genNone()) and some( " c" ) )
770
757
check sideEffects == 1
771
758
reset()
772
759
773
760
# 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
776
763
777
764
test " conditional continuation" :
778
765
when not compiles(some(" Hello world" ).? find('w' ).echo):
779
766
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
783
770
var evaluated = false
784
- if some(" team" ).? find('i' ) == - 1 :
771
+ if ( some(" team" ).? find('i' ) == ? some( - 1 )).isSome :
785
772
evaluated = true
786
773
check evaluated == true
787
774
evaluated = false
788
- if none(string ).? find('i' ) == - 1 :
775
+ if ( none(string ).? find('i' ) == ? some( - 1 )).isSome :
789
776
evaluated = true
790
777
check evaluated == false
791
778
0 commit comments