So, how do you do the equivalent of
proc xxx(param1: string, param2: int = nil, param3: int = nil)
if not param2.isNil():
# do something with param2
proc p(x: int, xIsSet: bool) = ...
or smth like
type IntOrNil = object
x: int
isSet: bool
proc p(x: IntOrNil) = (if x.isSet: echo x.x else: #[ something else ]# discard)
# additional procs may be added for such a type
proc `$`(x: IntOrNil): string = (if x.isSet: $x else: "nil")
converter x(x: IntOrNil): int =
if x.isSet:
return x.x
else:
raise newException(ValueError,"int to nil conversion")
# usage
p(IntOrNil(x: 5, isSet: false))
var v = IntOrNil(x: 5, isSet: true)
echo v+2
v.isSet = false
try:
echo v+2
except ValueError:
echo "v not set!"
# or so:
echo (try: $(v+2) except: "v not set!")
Like in most other languages.
Yet you can search github for "nim maybe" and the like queries, there were some libs.
UPD:
And a more Nim-specific way, with generics and static branching, no run-time overhead exc. file size:
type EmptyType = enum nothing
type IntOrNothing = int or EmptyType
proc pp(x: IntOrNothing) = (when x is int: echo x*10)
pp nothing
pp 5
For sake of completeness:
import options
proc xxx(param1: string, param2, param3 = none(int)) =
if param2.isSome:
echo "param2: ", param2.some
if param3.isSome:
echo "param3: ", param3.some
xxx("a", param2 = some(1))
xxx("b", param3 = some(2))
xxx("c", some(3), some(4))
xxx("d")
overloading may be an option as well:
proc a(x:int) =
discard
proc a() =
discard
a()
a(1)
Overloading has one major disadvantage:
proc hasTwoIntArgs(x = 3, y = 4) = ...
hasTwoIntArgs(x = -1) # args: x = -1, y = 4
hasTwoIntArgs(y = -1) # args: x = 3, y = -1
proc hasTwoIntArgsInvalid(x, y) = ...
proc hasTwoIntArgsInvalid(x) = hasTwoIntArgsInvalid(x, 4)
proc hasTwoIntArgsInvalid() = hasTwoIntArgsInvalid(3, 4)
# Kind of repetitive if you ask me...
hasTwoIntArgsInvalid(x = -1) # args: x = -1, y = 4 -- ok!
hasTwoIntArgsInvalid(y = -1) # Compile-time error! x unspecified!
That's the main reason why Option[int] seems much better. However, it's not transparent as you need to explicitly use some(...). However, you can write a thin wrapper over Option[T] with an converter to T so that you can write it transparently:
proc hasTwoOptionalArgs(x = inone(int), y = inone(int)) = ...
hasTwoOptionalArgs(x = 1) # x = isome(1), y = inone(int)
hasTwoOptionalArgs(y = 1) # x = inone(int), y = isome(1)
To combine what @def and @Udiknedormin have suggested:
import options
converter toOption[T](x:T):Option[T] = some(x)
proc xxx(param1: string, param2, param3 = none(int)) =
if param2.isSome:
echo "param2: ", param2.some
if param3.isSome:
echo "param3: ", param3.some
xxx("a", param2 = 1)
xxx("b", param3 = 2)
xxx("c", 3, 4)
xxx("d")
I'm curious if there are downsides to having that toOption converter hanging around. I can't think of any downsides. I'm curious if it would be worth having that converter included in the std lib. Thoughts?
I really don't like converters if they are not in a "lenient_ops" kind of module.
They introduce hard to debug bugs where the original input is converted and then not matched to something or ambiguous call because you have 2 automatic conversion possible (for example uint16 to int or uint16 to "SomeInteger" at best (because caught at compile-time) to runtime error.
They can even cause symbol visibility issues if they are not exported + slow down compilation significantly (see https://github.com/mratsim/Arraymancer/issues/394)