documented untyped/typed meta-types
This commit is contained in:
parent
b6374f1f68
commit
f9ad735e55
2 changed files with 93 additions and 27 deletions
|
|
@ -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.
|
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
|
In the following example, it would be seen as a literal number '3.0' of type
|
||||||
'float64' if not quoted, if uncertain - quote it:
|
'float64' if not quoted, if uncertain - quote it:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
import "gfx/3d/somemodule"
|
import "gfx/3d/somemodule"
|
||||||
|
|
||||||
|
|
||||||
Scope rules
|
Scope rules
|
||||||
-----------
|
-----------
|
||||||
Identifiers are valid from the point of their declaration until the end of
|
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:
|
Example:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
template `!=` (a, b: expr): expr =
|
template `!=` (a, b: untyped): untyped =
|
||||||
# this definition exists in the System module
|
# this definition exists in the System module
|
||||||
not (a == b)
|
not (a == b)
|
||||||
|
|
||||||
|
|
@ -23,50 +23,56 @@ templates:
|
||||||
| ``a in b`` is transformed into ``contains(b, a)``.
|
| ``a in b`` is transformed into ``contains(b, a)``.
|
||||||
| ``notin`` and ``isnot`` have the obvious meanings.
|
| ``notin`` and ``isnot`` have the obvious meanings.
|
||||||
|
|
||||||
The "types" of templates can be the symbols ``expr`` (stands for *expression*),
|
The "types" of templates can be the symbols ``untyped``,
|
||||||
``stmt`` (stands for *statement*) or ``typedesc`` (stands for *type
|
``typed`` or ``typedesc`` (stands for *type
|
||||||
description*). These are "meta types", they can only be used in certain
|
description*). These are "meta types", they can only be used in certain
|
||||||
contexts. Real types can be used too; this implies that expressions are
|
contexts. Real types can be used too; this implies that ``typed`` expressions
|
||||||
expected.
|
are expected.
|
||||||
|
|
||||||
|
|
||||||
Ordinary vs immediate templates
|
Typed vs untyped parameters
|
||||||
-------------------------------
|
---------------------------
|
||||||
|
|
||||||
There are two different kinds of templates: immediate templates and
|
An ``untyped`` parameter means that symbol lookups and type resolution is not
|
||||||
ordinary templates. Ordinary templates take part in overloading resolution. As
|
performed before the expression is passed to the template. This means that for
|
||||||
such their arguments need to be type checked before the template is invoked.
|
example *undeclared* identifiers can be passed to the template:
|
||||||
So ordinary templates cannot receive undeclared identifiers:
|
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
|
||||||
template declareInt(x: expr) =
|
template declareInt(x: untyped) =
|
||||||
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.} =
|
|
||||||
var x: int
|
var x: int
|
||||||
|
|
||||||
declareInt(x) # valid
|
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
|
Passing a code block to a template
|
||||||
----------------------------------
|
----------------------------------
|
||||||
|
|
||||||
If there is a ``stmt`` parameter it should be the last in the template
|
You can pass a block of statements as a last parameter to a template via a
|
||||||
declaration, because statements are passed to a template via a
|
|
||||||
special ``:`` syntax:
|
special ``:`` syntax:
|
||||||
|
|
||||||
.. code-block:: nim
|
.. code-block:: nim
|
||||||
|
template withFile(f, fn, mode, actions: untyped): untyped =
|
||||||
template withFile(f, fn, mode: expr, actions: stmt): stmt {.immediate.} =
|
|
||||||
var f: File
|
var f: File
|
||||||
if open(f, fn, mode):
|
if open(f, fn, mode):
|
||||||
try:
|
try:
|
||||||
|
|
@ -84,6 +90,64 @@ In the example the two ``writeLine`` statements are bound to the ``actions``
|
||||||
parameter.
|
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
|
Symbol binding in templates
|
||||||
---------------------------
|
---------------------------
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue