mirror of
https://github.com/nim-lang/Nim.git
synced 2026-04-19 05:50:30 +00:00
documented untyped/typed meta-types
This commit is contained in:
@@ -152,9 +152,11 @@ In module related statements, if any part of the module name /
|
||||
path begins with a number, you may have to quote it in double quotes.
|
||||
In the following example, it would be seen as a literal number '3.0' of type
|
||||
'float64' if not quoted, if uncertain - quote it:
|
||||
|
||||
.. code-block:: nim
|
||||
import "gfx/3d/somemodule"
|
||||
|
||||
|
||||
Scope rules
|
||||
-----------
|
||||
Identifiers are valid from the point of their declaration until the end of
|
||||
|
||||
@@ -10,7 +10,7 @@ The syntax to *invoke* a template is the same as calling a procedure.
|
||||
Example:
|
||||
|
||||
.. code-block:: nim
|
||||
template `!=` (a, b: expr): expr =
|
||||
template `!=` (a, b: untyped): untyped =
|
||||
# this definition exists in the System module
|
||||
not (a == b)
|
||||
|
||||
@@ -23,50 +23,56 @@ templates:
|
||||
| ``a in b`` is transformed into ``contains(b, a)``.
|
||||
| ``notin`` and ``isnot`` have the obvious meanings.
|
||||
|
||||
The "types" of templates can be the symbols ``expr`` (stands for *expression*),
|
||||
``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type
|
||||
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 expressions are
|
||||
expected.
|
||||
contexts. Real types can be used too; this implies that ``typed`` expressions
|
||||
are expected.
|
||||
|
||||
|
||||
Ordinary vs immediate templates
|
||||
-------------------------------
|
||||
Typed vs untyped parameters
|
||||
---------------------------
|
||||
|
||||
There are two different kinds of templates: immediate templates and
|
||||
ordinary templates. Ordinary templates take part in overloading resolution. As
|
||||
such their arguments need to be type checked before the template is invoked.
|
||||
So ordinary templates cannot receive undeclared identifiers:
|
||||
An ``untyped`` parameter means that symbol lookups and type resolution is not
|
||||
performed before the expression is passed to the template. This means that for
|
||||
example *undeclared* identifiers can be passed to the template:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
template declareInt(x: expr) =
|
||||
var x: int
|
||||
|
||||
declareInt(x) # error: unknown identifier: 'x'
|
||||
|
||||
An ``immediate`` template does not participate in overload resolution and so
|
||||
its arguments are not checked for semantics before invocation. So they can
|
||||
receive undeclared identifiers:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
template declareInt(x: expr) {.immediate.} =
|
||||
template declareInt(x: untyped) =
|
||||
var x: int
|
||||
|
||||
declareInt(x) # valid
|
||||
x = 3
|
||||
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
template declareInt(x: typed) =
|
||||
var x: int
|
||||
|
||||
declareInt(x) # invalid, because x has not been declared and so has no type
|
||||
|
||||
A template where every parameter is ``untyped`` is called an `immediate`:idx:
|
||||
template. For historical reasons templates can be explicitly annotated with
|
||||
an ``immediate`` pragma and then these templates do not take part in
|
||||
overloading resolution and the parameters' types are *ignored* by the
|
||||
compiler. Explicit immediate templates are about to be deprecated in later
|
||||
versions of the compiler.
|
||||
|
||||
**Note**: For historical reasons ``stmt`` is an alias for ``typed`` and
|
||||
``expr`` an alias for ``untyped``, but new code should use the newer,
|
||||
clearer names.
|
||||
|
||||
|
||||
Passing a code block to a template
|
||||
----------------------------------
|
||||
|
||||
If there is a ``stmt`` parameter it should be the last in the template
|
||||
declaration, because statements are passed to a template via a
|
||||
You can pass a block of statements as a last parameter to a template via a
|
||||
special ``:`` syntax:
|
||||
|
||||
.. code-block:: nim
|
||||
|
||||
template withFile(f, fn, mode: expr, actions: stmt): stmt {.immediate.} =
|
||||
template withFile(f, fn, mode, actions: untyped): untyped =
|
||||
var f: File
|
||||
if open(f, fn, mode):
|
||||
try:
|
||||
@@ -84,6 +90,64 @@ In the example the two ``writeLine`` statements are bound to the ``actions``
|
||||
parameter.
|
||||
|
||||
|
||||
Usually to pass a block of code to a template the parameter that accepts
|
||||
the block needs to be of type ``untyped``. Because symbol lookups are then
|
||||
delayed until template instantiation time:
|
||||
|
||||
.. code-block:: nim
|
||||
template t(body: typed) =
|
||||
block:
|
||||
body
|
||||
|
||||
t:
|
||||
var i = 1
|
||||
echo i
|
||||
|
||||
t:
|
||||
var i = 2 # fails with 'attempt to redeclare i'
|
||||
echo i
|
||||
|
||||
The above code fails with the mysterious error message that ``i`` has already
|
||||
been declared. The reason for this is that the ``var i = ...`` bodies need to
|
||||
be type-checked before they are passed to the ``body`` parameter and type
|
||||
checking in Nim implies symbol lookups. For the symbol lookups to succeed
|
||||
``i`` needs to be added to the current (i.e. outer) scope. After type checking
|
||||
these additions to the symbol table are not rolled back (for better or worse).
|
||||
The same code works with ``untyped`` as the passed body is not required to be
|
||||
type-checked:
|
||||
|
||||
.. code-block:: nim
|
||||
template t(body: untyped) =
|
||||
block:
|
||||
body
|
||||
|
||||
t:
|
||||
var i = 1
|
||||
echo i
|
||||
|
||||
t:
|
||||
var i = 2 # compiles
|
||||
echo i
|
||||
|
||||
|
||||
Varargs of untyped
|
||||
------------------
|
||||
|
||||
In addition to the ``untyped`` meta-type that prevents type checking there is
|
||||
also ``varargs[untyped]`` so that not even the number of parameters is fixed:
|
||||
|
||||
.. code-block:: nim
|
||||
template hideIdentifiers(x: varargs[untyped]) = discard
|
||||
|
||||
hideIdentifiers(undeclared1, undeclared2)
|
||||
|
||||
However, since a template cannot iterate over varargs, this feature is
|
||||
generally much more useful for macros.
|
||||
|
||||
**Note**: For historical reasons ``varargs[expr]`` is not equivalent
|
||||
to ``varargs[untyped]``.
|
||||
|
||||
|
||||
Symbol binding in templates
|
||||
---------------------------
|
||||
|
||||
|
||||
Reference in New Issue
Block a user