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

This commit is contained in:
Zahary Karadjov 2018-04-23 22:30:23 +03:00
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 - 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

View file

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

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

View file

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