documented untyped/typed meta-types

This commit is contained in:
Araq
2015-11-10 22:51:03 +01:00
parent b6374f1f68
commit f9ad735e55
2 changed files with 93 additions and 27 deletions

View File

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

View File

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