Nim/doc/manual/typedesc.txt
2015-03-23 13:10:19 +01:00

127 lines
3.9 KiB
Text

Special Types
=============
static[T]
---------
**Note**: static[T] is still in development.
As their name suggests, static parameters must be known at compile-time:
.. code-block:: nim
proc precompiledRegex(pattern: static[string]): RegEx =
var res {.global.} = re(pattern)
return res
precompiledRegex("/d+") # Replaces the call with a precompiled
# regex, stored in a global variable
precompiledRegex(paramStr(1)) # Error, command-line options
# are not known at compile-time
For the purposes of code generation, all static params are treated as
generic params - the proc will be compiled separately for each unique
supplied value (or combination of values).
Static params can also appear in the signatures of generic types:
.. code-block:: nim
type
Matrix[M,N: static[int]; T: Number] = array[0..(M*N - 1), T]
# Note how `Number` is just a type constraint here, while
# `static[int]` requires us to supply a compile-time int value
AffineTransform2D[T] = Matrix[3, 3, T]
AffineTransform3D[T] = Matrix[4, 4, T]
var m1: AffineTransform3D[float] # OK
var m2: AffineTransform2D[string] # Error, `string` is not a `Number`
typedesc
--------
`typedesc` is a special type allowing one to treat types as compile-time values
(i.e. if types are compile-time values and all values have a type, then
typedesc must be their type).
When used as a regular proc param, typedesc acts as a type class. The proc
will be instantiated for each unique type parameter and one can refer to the
instantiation type using the param name:
.. code-block:: nim
proc new(T: typedesc): ref T =
echo "allocating ", T.name
new(result)
var n = Node.new
var tree = new(BinaryTree[int])
When multiple typedesc params are present, they act like a distinct type class
(i.e. they will bind freely to different types). To force a bind-once behavior
one can use a named alias or an explicit `typedesc` generic param:
.. code-block:: nim
proc acceptOnlyTypePairs[T: typedesc, U: typedesc](A, B: T; C, D: U)
Once bound, typedesc params can appear in the rest of the proc signature:
.. code-block:: nim
template declareVariableWithType(T: typedesc, value: T) =
var x: T = value
declareVariableWithType int, 42
When used with macros and .compileTime. procs on the other hand, the compiler
does not need to instantiate the code multiple times, because types then can be
manipulated using the unified internal symbol representation. In such context
typedesc acts as any other type. One can create variables, store typedesc
values inside containers and so on. For example, here is how one can create
a type-safe wrapper for the unsafe `printf` function from C:
.. code-block:: nim
macro safePrintF(formatString: string{lit}, args: varargs[expr]): expr =
var i = 0
for c in formatChars(formatString):
var expectedType = case c
of 'c': char
of 'd', 'i', 'x', 'X': int
of 'f', 'e', 'E', 'g', 'G': float
of 's': string
of 'p': pointer
else: EOutOfRange
var actualType = args[i].getType
inc i
if expectedType == EOutOfRange:
error c & " is not a valid format character"
elif expectedType != actualType:
error "type mismatch for argument ", i, ". expected type: ",
expectedType.name, ", actual type: ", actualType.name
# keep the original callsite, but use cprintf instead
result = callsite()
result[0] = newIdentNode(!"cprintf")
Overload resolution can be further influenced by constraining the set of
types that will match the typedesc param:
.. code-block:: nim
template maxval(T: typedesc[int]): int = high(int)
template maxval(T: typedesc[float]): float = Inf
var i = int.maxval
var f = float.maxval
var s = string.maxval # error, maxval is not implemented for string
The constraint can be a concrete type or a type class.