mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-03 11:42:33 +00:00
document the new type[T] and static[T] features
This commit is contained in:
@@ -116,6 +116,13 @@
|
||||
- In order to make ``for`` loops and iterators more flexible to use Nim now
|
||||
supports so called "for-loop macros". See
|
||||
the `manual <manual.html#macros-for-loop-macros>`_ for more details.
|
||||
- the `typedesc` special type has been renamed to just `type`.
|
||||
- `static` and `type` are now also modifiers similar to `ref` and `ptr`.
|
||||
They denote the special types `static[T]` and `type[T]`.
|
||||
- Forcing compile-time evaluation with `static` now supports specifying
|
||||
the desired target type (as a concrete type or as a type class)
|
||||
- The `type` operator now supports checking that the supplied expression
|
||||
matches an expected type constraint.
|
||||
|
||||
### Language changes
|
||||
|
||||
|
||||
@@ -303,9 +303,9 @@ symbols in the `system module <system.html>`_.
|
||||
`#len,seq[T] <system.html#len,seq[T]>`_
|
||||
* ``iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}`` **=>**
|
||||
`#pairs.i,seq[T] <system.html#pairs.i,seq[T]>`_
|
||||
* ``template newException[](exceptn: typedesc; message: string): expr`` **=>**
|
||||
`#newException.t,typedesc,string
|
||||
<system.html#newException.t,typedesc,string>`_
|
||||
* ``template newException[](exceptn: type; message: string): expr`` **=>**
|
||||
`#newException.t,type,string
|
||||
<system.html#newException.t,type,string>`_
|
||||
|
||||
|
||||
Index (idx) file format
|
||||
|
||||
156
doc/manual.rst
156
doc/manual.rst
@@ -1732,7 +1732,7 @@ But it seems all this boilerplate code needs to be repeated for the ``Euro``
|
||||
currency. This can be solved with templates_.
|
||||
|
||||
.. code-block:: nim
|
||||
template additive(typ: typedesc) =
|
||||
template additive(typ: type) =
|
||||
proc `+` *(x, y: typ): typ {.borrow.}
|
||||
proc `-` *(x, y: typ): typ {.borrow.}
|
||||
|
||||
@@ -1740,13 +1740,13 @@ currency. This can be solved with templates_.
|
||||
proc `+` *(x: typ): typ {.borrow.}
|
||||
proc `-` *(x: typ): typ {.borrow.}
|
||||
|
||||
template multiplicative(typ, base: typedesc) =
|
||||
template multiplicative(typ, base: type) =
|
||||
proc `*` *(x: typ, y: base): typ {.borrow.}
|
||||
proc `*` *(x: base, y: typ): typ {.borrow.}
|
||||
proc `div` *(x: typ, y: base): typ {.borrow.}
|
||||
proc `mod` *(x: typ, y: base): typ {.borrow.}
|
||||
|
||||
template comparable(typ: typedesc) =
|
||||
template comparable(typ: type) =
|
||||
proc `<` * (x, y: typ): bool {.borrow.}
|
||||
proc `<=` * (x, y: typ): bool {.borrow.}
|
||||
proc `==` * (x, y: typ): bool {.borrow.}
|
||||
@@ -2396,7 +2396,7 @@ argument's resolution:
|
||||
rem unresolvedExpression(undeclaredIdentifier)
|
||||
|
||||
``untyped`` and ``varargs[untyped]`` are the only metatype that are lazy in this sense, the other
|
||||
metatypes ``typed`` and ``typedesc`` are not lazy.
|
||||
metatypes ``typed`` and ``type`` are not lazy.
|
||||
|
||||
|
||||
Varargs matching
|
||||
@@ -4274,29 +4274,6 @@ therefore very useful for type specialization within generic code:
|
||||
deletedKeys: seq[bool]
|
||||
|
||||
|
||||
Type operator
|
||||
-------------
|
||||
|
||||
The ``type`` (in many other languages called `typeof`:idx:) operator can
|
||||
be used to get the type of an expression:
|
||||
|
||||
.. code-block:: nim
|
||||
var x = 0
|
||||
var y: type(x) # y has type int
|
||||
|
||||
If ``type`` is used to determine the result type of a proc/iterator/converter
|
||||
call ``c(X)`` (where ``X`` stands for a possibly empty list of arguments), the
|
||||
interpretation where ``c`` is an iterator is preferred over the
|
||||
other interpretations:
|
||||
|
||||
.. code-block:: nim
|
||||
import strutils
|
||||
|
||||
# strutils contains both a ``split`` proc and iterator, but since an
|
||||
# an iterator is the preferred interpretation, `y` has the type ``string``:
|
||||
var y: type("a b c".split)
|
||||
|
||||
|
||||
Type Classes
|
||||
------------
|
||||
|
||||
@@ -4450,10 +4427,10 @@ the presence of callable symbols with specific signatures:
|
||||
OutputStream = concept var s
|
||||
s.write(string)
|
||||
|
||||
In order to check for symbols accepting ``typedesc`` params, you must prefix
|
||||
the type with an explicit ``type`` modifier. The named instance of the type,
|
||||
following the ``concept`` keyword is also considered an explicit ``typedesc``
|
||||
value that will be matched only as a type.
|
||||
In order to check for symbols accepting ``type`` params, you must prefix
|
||||
the type with the explicit ``type`` modifier. The named instance of the
|
||||
type, following the ``concept`` keyword is also considered to have the
|
||||
explicit modifier and will be matched only as a type.
|
||||
|
||||
.. code-block:: nim
|
||||
type
|
||||
@@ -4513,7 +4490,7 @@ The concept types can be parametric just like the regular generic types:
|
||||
import typetraits
|
||||
|
||||
type
|
||||
AnyMatrix*[R, C: static[int]; T] = concept m, var mvar, type M
|
||||
AnyMatrix*[R, C: static int; T] = concept m, var mvar, type M
|
||||
M.ValueType is T
|
||||
M.Rows == R
|
||||
M.Cols == C
|
||||
@@ -4523,7 +4500,7 @@ The concept types can be parametric just like the regular generic types:
|
||||
|
||||
type TransposedType = stripGenericParams(M)[C, R, T]
|
||||
|
||||
AnySquareMatrix*[N: static[int], T] = AnyMatrix[N, N, T]
|
||||
AnySquareMatrix*[N: static int, T] = AnyMatrix[N, N, T]
|
||||
|
||||
AnyTransform3D* = AnyMatrix[4, 4, float]
|
||||
|
||||
@@ -4542,7 +4519,7 @@ The concept types can be parametric just like the regular generic types:
|
||||
### matrix.nim
|
||||
|
||||
type
|
||||
Matrix*[M, N: static[int]; T] = object
|
||||
Matrix*[M, N: static int; T] = object
|
||||
data: array[M*N, T]
|
||||
|
||||
proc `[]`*(M: Matrix; m, n: int): M.T =
|
||||
@@ -4554,7 +4531,7 @@ The concept types can be parametric just like the regular generic types:
|
||||
# Adapt the Matrix type to the concept's requirements
|
||||
template Rows*(M: type Matrix): expr = M.M
|
||||
template Cols*(M: type Matrix): expr = M.N
|
||||
template ValueType*(M: type Matrix): typedesc = M.T
|
||||
template ValueType*(M: type Matrix): type = M.T
|
||||
|
||||
-------------
|
||||
### usage.nim
|
||||
@@ -4582,7 +4559,7 @@ operator and also when types dependent on them are being matched:
|
||||
|
||||
.. code-block:: nim
|
||||
type
|
||||
MatrixReducer[M, N: static[int]; T] = concept x
|
||||
MatrixReducer[M, N: static int; T] = concept x
|
||||
x.reduce(SquareMatrix[N, T]) is array[M, int]
|
||||
|
||||
The Nim compiler includes a simple linear equation solver, allowing it to
|
||||
@@ -4771,12 +4748,12 @@ object inheritance syntax involving the ``of`` keyword:
|
||||
|
||||
# the varargs param will here be converted to an array of StringRefValues
|
||||
# the proc will have only two instantiations for the two character types
|
||||
proc log(format: static[string], varargs[StringRef])
|
||||
proc log(format: static string, varargs[StringRef])
|
||||
|
||||
# this proc will allow char and wchar values to be mixed in
|
||||
# the same call at the cost of additional instantiations
|
||||
# the varargs param will be converted to a tuple
|
||||
proc log(format: static[string], varargs[distinct StringRef])
|
||||
proc log(format: static string, varargs[distinct StringRef])
|
||||
|
||||
|
||||
..
|
||||
@@ -4940,9 +4917,8 @@ templates:
|
||||
| ``notin`` and ``isnot`` have the obvious meanings.
|
||||
|
||||
The "types" of templates can be the symbols ``untyped``,
|
||||
``typed`` or ``typedesc`` (stands for *type
|
||||
description*). These are "meta types", they can only be used in certain
|
||||
contexts. Real types can be used too; this implies that ``typed`` expressions
|
||||
``typed`` or ``type``. These are "meta types", they can only be used in certain
|
||||
contexts. Regular types can be used too; this implies that ``typed`` expressions
|
||||
are expected.
|
||||
|
||||
|
||||
@@ -5109,7 +5085,7 @@ In templates identifiers can be constructed with the backticks notation:
|
||||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
|
||||
template typedef(name: untyped, typ: typedesc) =
|
||||
template typedef(name: untyped, typ: type) =
|
||||
type
|
||||
`T name`* {.inject.} = typ
|
||||
`P name`* {.inject.} = ref `T name`
|
||||
@@ -5171,7 +5147,7 @@ template cannot be accessed in the instantiation context:
|
||||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
|
||||
template newException*(exceptn: typedesc, message: string): untyped =
|
||||
template newException*(exceptn: type, message: string): untyped =
|
||||
var
|
||||
e: ref exceptn # e is implicitly gensym'ed here
|
||||
new(e)
|
||||
@@ -5493,7 +5469,7 @@ As their name suggests, static parameters must be known at compile-time:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc precompiledRegex(pattern: static[string]): RegEx =
|
||||
proc precompiledRegex(pattern: static string): RegEx =
|
||||
var res {.global.} = re(pattern)
|
||||
return res
|
||||
|
||||
@@ -5513,9 +5489,9 @@ 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]
|
||||
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
|
||||
# `static int` requires us to supply a compile-time int value
|
||||
|
||||
AffineTransform2D[T] = Matrix[3, 3, T]
|
||||
AffineTransform3D[T] = Matrix[4, 4, T]
|
||||
@@ -5523,53 +5499,75 @@ Static params can also appear in the signatures of generic types:
|
||||
var m1: AffineTransform3D[float] # OK
|
||||
var m2: AffineTransform2D[string] # Error, `string` is not a `Number`
|
||||
|
||||
Please note that ``static T`` is just a syntactic convenience for the
|
||||
underlying generic type ``static[T]``. This means that you can omit the
|
||||
type param to obtain the type class of all values, known at compile-time
|
||||
and you can restrict the matched values by instantiating ``static`` with
|
||||
another type class.
|
||||
|
||||
typedesc
|
||||
--------
|
||||
You can force the evaluation of a certain expression at compile-time by
|
||||
coercing it to a corresponding ``static`` type:
|
||||
|
||||
`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).
|
||||
.. code-block:: nim
|
||||
import math
|
||||
|
||||
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:
|
||||
echo static(fac(5)), " ", static[bool](16.isPowerOfTwo)
|
||||
|
||||
The complier will report any failure to evaluate the expression or a
|
||||
possible type mismatch error.
|
||||
|
||||
type[T]
|
||||
-------
|
||||
|
||||
In many contexts, Nim allows you to treat the names of types as regular
|
||||
values. These values exists only during the compilation phase, but since
|
||||
all values must have a type, ``type`` is considered their special type.
|
||||
|
||||
``type`` acts like a generic type. For instance, the type of the symbol
|
||||
``int`` is ``type[int]``. Just like with regular generic types, when the
|
||||
generic param is ommited, ``type`` denotes the type class of all types.
|
||||
As a syntactic convenience, you can also use ``type`` as a modifier.
|
||||
``type int`` is considered the same as ``type[int]``.
|
||||
|
||||
Procs featuring ``type`` params will be considered implicitly generic.
|
||||
They will be instantiated for each unique combination of supplied types
|
||||
and within the body of the proc, the name of each param will refer to
|
||||
the bound concrete type:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
proc new(T: typedesc): ref T =
|
||||
proc new(T: type): ref T =
|
||||
echo "allocating ", T.name
|
||||
new(result)
|
||||
|
||||
var n = Node.new
|
||||
var tree = new(BinaryTree[int])
|
||||
|
||||
When multiple typedesc params are present, they will bind freely to different
|
||||
types. To force a bind-once behavior
|
||||
one can use an explicit ``typedesc[T]`` generic param:
|
||||
When multiple type params are present, they will bind freely to different
|
||||
types. To force a bind-once behavior one can use an explicit generic param:
|
||||
|
||||
.. code-block:: nim
|
||||
proc acceptOnlyTypePairs[T, U](A, B: typedesc[T]; C, D: typedesc[U])
|
||||
proc acceptOnlyTypePairs[T, U](A, B: type[T]; C, D: type[U])
|
||||
|
||||
Once bound, typedesc params can appear in the rest of the proc signature:
|
||||
Once bound, type params can appear in the rest of the proc signature:
|
||||
|
||||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
|
||||
template declareVariableWithType(T: typedesc, value: T) =
|
||||
template declareVariableWithType(T: type, value: T) =
|
||||
var x: T = value
|
||||
|
||||
declareVariableWithType int, 42
|
||||
|
||||
|
||||
Overload resolution can be further influenced by constraining the set of
|
||||
types that will match the typedesc param:
|
||||
types that will match the type param:
|
||||
|
||||
.. code-block:: nim
|
||||
:test: "nim c $1"
|
||||
|
||||
template maxval(T: typedesc[int]): int = high(int)
|
||||
template maxval(T: typedesc[float]): float = Inf
|
||||
template maxval(T: type int): int = high(int)
|
||||
template maxval(T: type float): float = Inf
|
||||
|
||||
var i = int.maxval
|
||||
var f = float.maxval
|
||||
@@ -5578,7 +5576,35 @@ types that will match the typedesc param:
|
||||
|
||||
The constraint can be a concrete type or a type class.
|
||||
|
||||
type operator
|
||||
-------------
|
||||
|
||||
You can obtain the type of a given expression by constructing a ``type``
|
||||
value from it (in many other languages this is known as the `typeof`:idx:
|
||||
operator):
|
||||
|
||||
.. code-block:: nim
|
||||
var x = 0
|
||||
var y: type(x) # y has type int
|
||||
|
||||
You may add a constraint to the resulting type to trigger a compile-time error
|
||||
if the expression doesn't have the expected type:
|
||||
|
||||
.. code-block:: nim
|
||||
var x = 0
|
||||
var y: type[object](x) # Error: type mismatch: got <int> but expected 'object'
|
||||
|
||||
If ``type`` is used to determine the result type of a proc/iterator/converter
|
||||
call ``c(X)`` (where ``X`` stands for a possibly empty list of arguments), the
|
||||
interpretation where ``c`` is an iterator is preferred over the
|
||||
other interpretations:
|
||||
|
||||
.. code-block:: nim
|
||||
import strutils
|
||||
|
||||
# strutils contains both a ``split`` proc and iterator, but since an
|
||||
# an iterator is the preferred interpretation, `y` has the type ``string``:
|
||||
var y: type("a b c".split)
|
||||
|
||||
|
||||
Special Operators
|
||||
@@ -7458,7 +7484,7 @@ Custom pragmas are defined using templates annotated with pragma ``pragma``:
|
||||
.. code-block:: nim
|
||||
template dbTable(name: string, table_space: string = "") {.pragma.}
|
||||
template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
|
||||
template dbForeignKey(t: typedesc) {.pragma.}
|
||||
template dbForeignKey(t: type) {.pragma.}
|
||||
template dbIgnore {.pragma.}
|
||||
|
||||
|
||||
|
||||
@@ -618,9 +618,9 @@ Turning the ``log`` proc into a template solves this problem:
|
||||
log("x has the value: " & $x)
|
||||
|
||||
The parameters' types can be ordinary types or the meta types ``untyped``,
|
||||
``typed``, or ``typedesc``.
|
||||
``typedesc`` stands for *type description*, and ``untyped`` means symbol lookups and
|
||||
type resolution is not performed before the expression is passed to the template.
|
||||
``typed``, or ``type``. ``type`` suggests that only a type symbol may be given
|
||||
as an argument, and ``untyped`` means symbol lookups and type resolution is not
|
||||
performed before the expression is passed to the template.
|
||||
|
||||
If the template has no explicit return type,
|
||||
``void`` is used for consistency with procs and methods.
|
||||
|
||||
Reference in New Issue
Block a user