I would like to use it more--I've seen it brought up as a killer feature--but 
I'm having some trouble understanding the borders of this little kingdom in Nim.

Specifically I don't understand how/when/to what degree `range[a..b]` acts like 
a "real" type (like int8) and when it doesn't. I also do not have a clear image 
of when/how/if it is preserved through generics and meta-programming (see 
below). I don't understand why it isn't a Nim type class like `enum`, even 
though it almost, kind-of is (ibid).

Here are some smaller examples of some explorations I did, trying to learn, but 
at each step I had trouble finding consistency; each block below has an 
implicit question of "is this undefined behavior or is it desired/designed to 
be just-so?"
    
    
    type SmallNat = range[0..9]
    
    static:
      # This erases the `range[a..b]` and happily assigns values outside of the 
range.
      proc iwith1(v: var SomeOrdinal, n: int): auto =
        if n >= int(v.low) and n <= int(v.high):
          v = typeof(v)(n)
      
      var x: SmallNat = 5
      x.iwith1(1_000_000_000)
      assert x is SmallNat and x >= 10 # What does this even mean?
    
    static:
      # This is the same, even though we're allowed to role-play as if `range` 
is a "type class" so long as we use a sum type.
      proc iwith2(v: range | void, n: int): typeof(v) =
        if n >= int(v.low) and n <= int(v.high): typeof(v)(n) else: v
      
      const q = SmallNat(1).iwith2(1_000_000_000)
      assert q isnot SmallNat and q is int and q >= 10
    
    static:
      # I'm not sure what `range` does or what it is supposed to mean in the 
sum type, because `range` is
      # explicitly not a type class.
      assert not (compiles do:
        # Error: invalid type: 'range' in this context: 'proc (x: range)' for 
proc
        proc rbar(x: range) =
          discard)
    
    static:
      # Aside: the same confusing drunk-type-class semantics is found with 
`Ordinal`:
      assert compiles do:
        proc ofoo(x: Ordinal | float) =
          discard
        ofoo(1) # yup
        ofoo(littleEndian) # yup
        ofoo(1.2) # yup
      assert not (compiles do:
        # Error: invalid type: 'None' in this context: 'proc (x: Ordinal)' for 
proc
        #                         ^---- much more confusing error message 
though.
        proc obar(x: Ordinal) =
          discard)
    
    static:
      # Despite the aforementioned procs which erase the range, it *is* 
preserved when we use explicit
      # generics, even though I had the delusion this function should be 
semantically equivalent to `iwith1`?
      proc iwith3[T: SomeOrdinal](v: var T, n: int): auto =
        static: assert T is SomeOrdinal and T is range
        if n >= int(v.low) and n <= int(v.high):
          v = typeof(v)(n)
      
      var y: SmallNat = 3
      y.iwith3(1_000_000_000)
      assert y is SmallNat and y == 3
    
    static:
      # We're also allowed to say `T: range`, again role-playing as a type 
class, but now it seems
      # to...work?
      proc iwith4[Q: range](v: Q, n: int): auto =
        static: assert Q is range
        if n >= int(v.low) and n <= int(v.high): typeof(v)(n) else: v
      
      assert SmallNat(4).iwith4(1_000_000_000) == 4
      assert not compiles(9.iwith4(1_000_000_000)) # it does demand a real, 
living range[], not an int
    
    # Finally, this is actually closer to what I first tried/hoped would work,
    # assuming A, B would be inferred:
    proc iwith5[A, B: static[int]](v: range[A..B], n: int): auto =
      if n >= A and n <= B: range[A..B](n) else: v
    
    when not compiles(iwith5(range[1..12](7), 11)):
      static: echo ":("
    
    # It is however a rather strange function that seems to "trick" compiles():
    when (compiles do: assert iwith5[1,12](7, 1100) == 7):
      static: echo "LIES"
    
    # Error: conversion from int literal(7) to range <invalid value>..<invalid 
value>(int) is invalid
    # assert iwith5[1,12](7, 1100) == 7
    
    # Also, `range[]` seems to be completely ignored by `static[]':
    proc stat0(n: static[range[0..5]]): string =
      when n > 5:
        return "why"
      else:
        return $NimMajor
    proc stat1[n: static[SmallNat], T](xs: array[n, T]): int =
      return xs.len
    proc stat2[n: static[range[0..0]], T](xs: array[n, T]): int =
      return xs.len
    proc stat3[n: range[1..4], T](xs: array[n, T]): int = # probably makes no 
semantic sense, but it compiles?
      return xs.len
    
    static:
      assert stat0(999) == "why"
      assert stat1([1,1,1,1,1,1,1,1,1,1,1]) == 11
      assert stat2([1,1,1,1,1,1,1,1,1,1,1,0]) == 12
      assert stat3([1,1,1,1,1,1,1,1,1,1,1,2,2]) == 13
    
    
    Run

Reply via email to