See title. I've tried:
type
A = enum
a, b, c
Subrange = range[b..c]
#echo A.b in Subrange # doesn't compile
echo A.b in Subrange.low..Subrange.high # looks fine, except...
echo A.a in Subrange.low..Subrange.high # RangeDefect at runtime :(
echo A.a in Subrange.low.A..Subrange.high.A # finally works, but...really?
Am I missing something?
type
A = enum
X, Y, Z
B = Y..Z
doAssert Y isnot B
const Range = B.low..B.high
doAssert Y in Range
let b = B(Y)
doAssert b is B
doAssert b in Range
doAssert b is A
type SR = range[B.low..B.high]
doAssert Y isnot SR
let yr = SR.low
doAssert yr is SR
doAssert yr is B
# ...but requires conversion for direct comparison
doAssert yr.B == B.low
in check for ranges, is checks for types.
I think he's more interested in knowing why is the behavior different from the expected 0 notin 1..2
type
A = enum
a, b, c
Subrange = range[b..c]
assert 0 notin 1..2
try:
echo "A.a in Subrange.low..Subrange.high = " & $(A.a in Subrange.low..Subrange.high)
except RangeDefect:
echo "raises RangeDefect"
This outputs,
A.a in Subrange.low..Subrange.high = false
raises RangeDefect
My question is why it throws after evaluating correctly.
type A = enum
a
b
c
type Sub = range[b..c]
echo typeof(b)
echo typeof(Sub.low)
echo Sub.low is A
echo b in Sub.low..Sub.high
echo a in Sub.low..Sub.high
this outputs
A
Sub
true
true
false and crashes (2.2.4 and 2.3.1) with .
I suppose the defect is due to type mismatch, since Sub.low is A, there should be automatic casting? Looks like a bug to me.
Also, it is strange that the last line is printed, even though a defect is raised. However, it is not printed if --panics:on. This also looks strange.
low, high, and contains do not cast their arguments. They are written as generic procs, and pass around the types. Since a has type A and is not part of the type Sub, the compiler should report a range error. Why it waits until after completing the program is the real question.
echo 0 notin 1..5 # fine
echo 'a' notin 'b'..'f' # fine
echo 0 notin 'b'..'f' # error! This won't compile and run at all.
contains just checks the upper and lower bounds against the value.
proc contains*[U, V, W](s: HSlice[U, V], value: W): bool {.noSideEffect, inline.} =
result = s.a <= value and value <= s.b
The really interesting part is that you only have to cast the first value in the range. The following line runs and compiles without error as part of the main program.
echo a notin Sub.low.A..Sub.highWelcome to range, one of the more sad features of Nim :)
Things get implicitly converted to it with RangeDefect being raised at runtime, which is what is happening here - ie a implicitly is converted to Subrange which fails). What the compiler is actually evaluating is Subrange(A.a) in Subrange.low..Subrange.high
The only thing you really need to remember about range (and its friend Natural which unfortunately is all over the std lib) is to stay away from it in your own code.
The second best thing is to hope for https://github.com/nim-lang/Nim/issues/24706 to get implemented so that it can be turned into a compile-time error with --warningsAsError as soon as all code has been fixed to make the cast explicitly - there's a lot of that code however, due to said Natural usage in stdlib.