In certain contexts, like array[E, T] type declarations, Nim requires the expression E to be constant. A constant expression is defined as follows:
If a and b are constant expressions, so is a b and a. can be: • A type conversion that can be interpreted as a type annotation like int(4), MyTuple((a, b, c)), ObjRef(nil). • Unary minus system.- for the builtin types. • Unary system.not for the builtin types. • system.succ. • system.pred. can be: • system.+, system.-, system.*, system.mod, system.div, system.shr, system.shl, system.max, system.min. The current Nim implementation goes much farther than this definition and considers any expression to be constant that it can evaluate at compile-time via its powerful virtual machine. For example, the standard library’s math module contains: func createFactTable[N: static[int]]: array[N, int] = 1 result[0] = 1 for i in 1 ..< N: result[i] = result[i - 1] * i
117
func fac*(n: int): int =
n.runnableExamples: doAssert fac(0) == 1 doAssert fac(4) == 24 doAssert fac(10) == 3628800 const factTable = 2 when sizeof(int) == 2: createFactTable[5]() elif sizeof(int) == 4: createFactTable[13]() else: createFactTable[21]() assert(n >= 0, $n & " must not be negative.") assert(n < factTable.len, $n & " is too large to look up in the table") factTable[n] 3 1 createFactTable computes a lookup table at compile-time. 2 factTable is the lookup table. Its size depends on the size of the int type. 3 The fac implementation does not do any computation; instead the factTable is queried for the precomputed result. The mechanisms of compile-time evaluation are the foundation for Nim’s macro system.