document the new type[T] and static[T] features
This commit is contained in:
parent
a49b06a52a
commit
ea36e0ebbe
4 changed files with 104 additions and 71 deletions
|
|
@ -116,6 +116,13 @@
|
||||||
- In order to make ``for`` loops and iterators more flexible to use Nim now
|
- In order to make ``for`` loops and iterators more flexible to use Nim now
|
||||||
supports so called "for-loop macros". See
|
supports so called "for-loop macros". See
|
||||||
the `manual <manual.html#macros-for-loop-macros>`_ for more details.
|
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
|
### Language changes
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -303,9 +303,9 @@ symbols in the `system module <system.html>`_.
|
||||||
`#len,seq[T] <system.html#len,seq[T]>`_
|
`#len,seq[T] <system.html#len,seq[T]>`_
|
||||||
* ``iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}`` **=>**
|
* ``iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}`` **=>**
|
||||||
`#pairs.i,seq[T] <system.html#pairs.i,seq[T]>`_
|
`#pairs.i,seq[T] <system.html#pairs.i,seq[T]>`_
|
||||||
* ``template newException[](exceptn: typedesc; message: string): expr`` **=>**
|
* ``template newException[](exceptn: type; message: string): expr`` **=>**
|
||||||
`#newException.t,typedesc,string
|
`#newException.t,type,string
|
||||||
<system.html#newException.t,typedesc,string>`_
|
<system.html#newException.t,type,string>`_
|
||||||
|
|
||||||
|
|
||||||
Index (idx) file format
|
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_.
|
currency. This can be solved with templates_.
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
template additive(typ: typedesc) =
|
template additive(typ: type) =
|
||||||
proc `+` *(x, y: typ): typ {.borrow.}
|
proc `+` *(x, y: typ): typ {.borrow.}
|
||||||
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.}
|
||||||
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: typ, y: base): typ {.borrow.}
|
||||||
proc `*` *(x: base, y: typ): typ {.borrow.}
|
proc `*` *(x: base, y: typ): typ {.borrow.}
|
||||||
proc `div` *(x: typ, y: base): typ {.borrow.}
|
proc `div` *(x: typ, y: base): typ {.borrow.}
|
||||||
proc `mod` *(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.}
|
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)
|
rem unresolvedExpression(undeclaredIdentifier)
|
||||||
|
|
||||||
``untyped`` and ``varargs[untyped]`` are the only metatype that are lazy in this sense, the other
|
``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
|
Varargs matching
|
||||||
|
|
@ -4274,29 +4274,6 @@ therefore very useful for type specialization within generic code:
|
||||||
deletedKeys: seq[bool]
|
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
|
Type Classes
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
|
@ -4450,10 +4427,10 @@ the presence of callable symbols with specific signatures:
|
||||||
OutputStream = concept var s
|
OutputStream = concept var s
|
||||||
s.write(string)
|
s.write(string)
|
||||||
|
|
||||||
In order to check for symbols accepting ``typedesc`` params, you must prefix
|
In order to check for symbols accepting ``type`` params, you must prefix
|
||||||
the type with an explicit ``type`` modifier. The named instance of the type,
|
the type with the explicit ``type`` modifier. The named instance of the
|
||||||
following the ``concept`` keyword is also considered an explicit ``typedesc``
|
type, following the ``concept`` keyword is also considered to have the
|
||||||
value that will be matched only as a type.
|
explicit modifier and will be matched only as a type.
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
type
|
type
|
||||||
|
|
@ -4513,7 +4490,7 @@ The concept types can be parametric just like the regular generic types:
|
||||||
import typetraits
|
import typetraits
|
||||||
|
|
||||||
type
|
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.ValueType is T
|
||||||
M.Rows == R
|
M.Rows == R
|
||||||
M.Cols == C
|
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]
|
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]
|
AnyTransform3D* = AnyMatrix[4, 4, float]
|
||||||
|
|
||||||
|
|
@ -4542,7 +4519,7 @@ The concept types can be parametric just like the regular generic types:
|
||||||
### matrix.nim
|
### matrix.nim
|
||||||
|
|
||||||
type
|
type
|
||||||
Matrix*[M, N: static[int]; T] = object
|
Matrix*[M, N: static int; T] = object
|
||||||
data: array[M*N, T]
|
data: array[M*N, T]
|
||||||
|
|
||||||
proc `[]`*(M: Matrix; m, n: int): M.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
|
# Adapt the Matrix type to the concept's requirements
|
||||||
template Rows*(M: type Matrix): expr = M.M
|
template Rows*(M: type Matrix): expr = M.M
|
||||||
template Cols*(M: type Matrix): expr = M.N
|
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
|
### usage.nim
|
||||||
|
|
@ -4582,7 +4559,7 @@ operator and also when types dependent on them are being matched:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
type
|
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]
|
x.reduce(SquareMatrix[N, T]) is array[M, int]
|
||||||
|
|
||||||
The Nim compiler includes a simple linear equation solver, allowing it to
|
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 varargs param will here be converted to an array of StringRefValues
|
||||||
# the proc will have only two instantiations for the two character types
|
# 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
|
# this proc will allow char and wchar values to be mixed in
|
||||||
# the same call at the cost of additional instantiations
|
# the same call at the cost of additional instantiations
|
||||||
# the varargs param will be converted to a tuple
|
# 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.
|
| ``notin`` and ``isnot`` have the obvious meanings.
|
||||||
|
|
||||||
The "types" of templates can be the symbols ``untyped``,
|
The "types" of templates can be the symbols ``untyped``,
|
||||||
``typed`` or ``typedesc`` (stands for *type
|
``typed`` or ``type``. These are "meta types", they can only be used in certain
|
||||||
description*). These are "meta types", they can only be used in certain
|
contexts. Regular types can be used too; this implies that ``typed`` expressions
|
||||||
contexts. Real types can be used too; this implies that ``typed`` expressions
|
|
||||||
are expected.
|
are expected.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -5109,7 +5085,7 @@ In templates identifiers can be constructed with the backticks notation:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
:test: "nim c $1"
|
:test: "nim c $1"
|
||||||
|
|
||||||
template typedef(name: untyped, typ: typedesc) =
|
template typedef(name: untyped, typ: type) =
|
||||||
type
|
type
|
||||||
`T name`* {.inject.} = typ
|
`T name`* {.inject.} = typ
|
||||||
`P name`* {.inject.} = ref `T name`
|
`P name`* {.inject.} = ref `T name`
|
||||||
|
|
@ -5171,7 +5147,7 @@ template cannot be accessed in the instantiation context:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
:test: "nim c $1"
|
:test: "nim c $1"
|
||||||
|
|
||||||
template newException*(exceptn: typedesc, message: string): untyped =
|
template newException*(exceptn: type, message: string): untyped =
|
||||||
var
|
var
|
||||||
e: ref exceptn # e is implicitly gensym'ed here
|
e: ref exceptn # e is implicitly gensym'ed here
|
||||||
new(e)
|
new(e)
|
||||||
|
|
@ -5493,7 +5469,7 @@ As their name suggests, static parameters must be known at compile-time:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
proc precompiledRegex(pattern: static[string]): RegEx =
|
proc precompiledRegex(pattern: static string): RegEx =
|
||||||
var res {.global.} = re(pattern)
|
var res {.global.} = re(pattern)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
@ -5513,9 +5489,9 @@ Static params can also appear in the signatures of generic types:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
type
|
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
|
# 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]
|
AffineTransform2D[T] = Matrix[3, 3, T]
|
||||||
AffineTransform3D[T] = Matrix[4, 4, 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 m1: AffineTransform3D[float] # OK
|
||||||
var m2: AffineTransform2D[string] # Error, `string` is not a `Number`
|
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
|
.. code-block:: nim
|
||||||
(i.e. if types are compile-time values and all values have a type, then
|
import math
|
||||||
typedesc must be their type).
|
|
||||||
|
|
||||||
When used as a regular proc param, typedesc acts as a type class. The proc
|
echo static(fac(5)), " ", static[bool](16.isPowerOfTwo)
|
||||||
will be instantiated for each unique type parameter and one can refer to the
|
|
||||||
instantiation type using the param name:
|
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
|
.. code-block:: nim
|
||||||
|
|
||||||
proc new(T: typedesc): ref T =
|
proc new(T: type): ref T =
|
||||||
echo "allocating ", T.name
|
echo "allocating ", T.name
|
||||||
new(result)
|
new(result)
|
||||||
|
|
||||||
var n = Node.new
|
var n = Node.new
|
||||||
var tree = new(BinaryTree[int])
|
var tree = new(BinaryTree[int])
|
||||||
|
|
||||||
When multiple typedesc params are present, they will bind freely to different
|
When multiple type params are present, they will bind freely to different
|
||||||
types. To force a bind-once behavior
|
types. To force a bind-once behavior one can use an explicit generic param:
|
||||||
one can use an explicit ``typedesc[T]`` generic param:
|
|
||||||
|
|
||||||
.. code-block:: nim
|
.. 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
|
.. code-block:: nim
|
||||||
:test: "nim c $1"
|
:test: "nim c $1"
|
||||||
|
|
||||||
template declareVariableWithType(T: typedesc, value: T) =
|
template declareVariableWithType(T: type, value: T) =
|
||||||
var x: T = value
|
var x: T = value
|
||||||
|
|
||||||
declareVariableWithType int, 42
|
declareVariableWithType int, 42
|
||||||
|
|
||||||
|
|
||||||
Overload resolution can be further influenced by constraining the set of
|
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
|
.. code-block:: nim
|
||||||
:test: "nim c $1"
|
:test: "nim c $1"
|
||||||
|
|
||||||
template maxval(T: typedesc[int]): int = high(int)
|
template maxval(T: type int): int = high(int)
|
||||||
template maxval(T: typedesc[float]): float = Inf
|
template maxval(T: type float): float = Inf
|
||||||
|
|
||||||
var i = int.maxval
|
var i = int.maxval
|
||||||
var f = float.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.
|
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
|
Special Operators
|
||||||
|
|
@ -7458,7 +7484,7 @@ Custom pragmas are defined using templates annotated with pragma ``pragma``:
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
template dbTable(name: string, table_space: string = "") {.pragma.}
|
template dbTable(name: string, table_space: string = "") {.pragma.}
|
||||||
template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
|
template dbKey(name: string = "", primary_key: bool = false) {.pragma.}
|
||||||
template dbForeignKey(t: typedesc) {.pragma.}
|
template dbForeignKey(t: type) {.pragma.}
|
||||||
template dbIgnore {.pragma.}
|
template dbIgnore {.pragma.}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -618,9 +618,9 @@ Turning the ``log`` proc into a template solves this problem:
|
||||||
log("x has the value: " & $x)
|
log("x has the value: " & $x)
|
||||||
|
|
||||||
The parameters' types can be ordinary types or the meta types ``untyped``,
|
The parameters' types can be ordinary types or the meta types ``untyped``,
|
||||||
``typed``, or ``typedesc``.
|
``typed``, or ``type``. ``type`` suggests that only a type symbol may be given
|
||||||
``typedesc`` stands for *type description*, and ``untyped`` means symbol lookups and
|
as an argument, and ``untyped`` means symbol lookups and type resolution is not
|
||||||
type resolution is not performed before the expression is passed to the template.
|
performed before the expression is passed to the template.
|
||||||
|
|
||||||
If the template has no explicit return type,
|
If the template has no explicit return type,
|
||||||
``void`` is used for consistency with procs and methods.
|
``void`` is used for consistency with procs and methods.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue