manual split up into multiple files; documented the new concurrency system
This commit is contained in:
parent
e9dec2feed
commit
2c1f3f75f5
31 changed files with 6198 additions and 5901 deletions
146
doc/manual/typedesc.txt
Normal file
146
doc/manual/typedesc.txt
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
Special Types
|
||||
=============
|
||||
|
||||
static[T]
|
||||
---------
|
||||
|
||||
**Note**: static[T] is still in development.
|
||||
|
||||
As their name suggests, static params must be known at compile-time:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc precompiledRegex(pattern: static[string]): TRegEx =
|
||||
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).
|
||||
|
||||
Furthermore, the system module defines a `semistatic[T]` type than can be
|
||||
used to declare procs accepting both static and run-time values, which can
|
||||
optimize their body according to the supplied param using the `isStatic(p)`
|
||||
predicate:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
# The following proc will be compiled once for each unique static
|
||||
# value and also once for the case handling all run-time values:
|
||||
|
||||
proc re(pattern: semistatic[string]): TRegEx =
|
||||
when isStatic(pattern):
|
||||
result = precompiledRegex(pattern)
|
||||
else:
|
||||
result = compile(pattern)
|
||||
|
||||
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 = TNode.new
|
||||
var tree = new(TBinaryTree[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
|
||||
|
||||
# `type1` and `type2` are aliases for typedesc available from system.nim
|
||||
proc acceptOnlyTypePairs(A, B: type1; C, D: type2)
|
||||
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.
|
||||
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue