document the new type[T] and static[T] features

This commit is contained in:
Zahary Karadjov
2018-04-23 22:30:23 +03:00
parent a49b06a52a
commit ea36e0ebbe
4 changed files with 104 additions and 71 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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.}

View File

@@ -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.