mirror of
https://github.com/nim-lang/Nim.git
synced 2026-06-06 20:04:18 +00:00
documented new symbol binding rules for templates
This commit is contained in:
131
doc/manual.txt
131
doc/manual.txt
@@ -12,6 +12,8 @@ Nimrod Manual
|
||||
user to one/some of the other players, but the total amount seems to remain
|
||||
pretty much constant for a given task. -- Ran
|
||||
|
||||
|
||||
|
||||
About this document
|
||||
===================
|
||||
|
||||
@@ -1479,7 +1481,7 @@ But it seems all this boilerplate code needs to be repeated for the ``TEuro``
|
||||
currency. This can be solved with templates_.
|
||||
|
||||
.. code-block:: nimrod
|
||||
template Additive(typ: typedesc): stmt =
|
||||
template additive(typ: typedesc): stmt =
|
||||
proc `+` *(x, y: typ): typ {.borrow.}
|
||||
proc `-` *(x, y: typ): typ {.borrow.}
|
||||
|
||||
@@ -1487,26 +1489,26 @@ currency. This can be solved with templates_.
|
||||
proc `+` *(x: typ): typ {.borrow.}
|
||||
proc `-` *(x: typ): typ {.borrow.}
|
||||
|
||||
template Multiplicative(typ, base: typedesc): stmt =
|
||||
template multiplicative(typ, base: typedesc): stmt =
|
||||
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): stmt =
|
||||
template comparable(typ: typedesc): stmt =
|
||||
proc `<` * (x, y: typ): bool {.borrow.}
|
||||
proc `<=` * (x, y: typ): bool {.borrow.}
|
||||
proc `==` * (x, y: typ): bool {.borrow.}
|
||||
|
||||
template DefineCurrency(typ, base: expr): stmt =
|
||||
template defineCurrency(typ, base: expr): stmt =
|
||||
type
|
||||
typ* = distinct base
|
||||
Additive(typ)
|
||||
Multiplicative(typ, base)
|
||||
Comparable(typ)
|
||||
additive(typ)
|
||||
multiplicative(typ, base)
|
||||
comparable(typ)
|
||||
|
||||
DefineCurrency(TDollar, int)
|
||||
DefineCurrency(TEuro, int)
|
||||
defineCurrency(TDollar, int)
|
||||
defineCurrency(TEuro, int)
|
||||
|
||||
|
||||
Void type
|
||||
@@ -3440,13 +3442,41 @@ A symbol can be forced to be open by a `mixin`:idx: declaration:
|
||||
|
||||
.. code-block:: nimrod
|
||||
proc create*[T](): ref T =
|
||||
# there is no overloaded 'mixin' here, so we need to state that it's an
|
||||
# there is no overloaded 'init' here, so we need to state that it's an
|
||||
# open symbol explicitly:
|
||||
mixin init
|
||||
new result
|
||||
init result
|
||||
|
||||
|
||||
Bind statement
|
||||
--------------
|
||||
|
||||
The `bind`:idx: statement is the counterpart to the ``mixin`` statement. It
|
||||
can be used to explicitly declare identifiers that should be bound early (i.e.
|
||||
the identifiers should be looked up in the scope of the template/generic
|
||||
definition):
|
||||
|
||||
.. code-block:: nimrod
|
||||
# Module A
|
||||
var
|
||||
lastId = 0
|
||||
|
||||
template genId*: expr =
|
||||
bind lastId
|
||||
inc(lastId)
|
||||
lastId
|
||||
|
||||
.. code-block:: nimrod
|
||||
# Module B
|
||||
import A
|
||||
|
||||
echo genId()
|
||||
|
||||
But a ``bind`` is rarely useful because symbol binding from the definition
|
||||
scope is the default.
|
||||
|
||||
|
||||
Templates
|
||||
=========
|
||||
|
||||
@@ -3506,28 +3536,6 @@ receive undeclared identifiers:
|
||||
declareInt(x) # valid
|
||||
|
||||
|
||||
Scoping in templates
|
||||
--------------------
|
||||
|
||||
The template body does not open a new scope. To open a new scope a ``block``
|
||||
statement can be used:
|
||||
|
||||
.. code-block:: nimrod
|
||||
template declareInScope(x: expr, t: typedesc): stmt {.immediate.} =
|
||||
var x: t
|
||||
|
||||
template declareInNewScope(x: expr, t: typedesc): stmt {.immediate.} =
|
||||
# open a new scope:
|
||||
block:
|
||||
var x: t
|
||||
|
||||
declareInScope(a, int)
|
||||
a = 42 # works, `a` is known here
|
||||
|
||||
declareInNewScope(b, int)
|
||||
b = 42 # does not work, `b` is unknown
|
||||
|
||||
|
||||
Passing a code block to a template
|
||||
----------------------------------
|
||||
|
||||
@@ -3538,26 +3546,28 @@ special ``:`` syntax:
|
||||
.. code-block:: nimrod
|
||||
|
||||
template withFile(f, fn, mode: expr, actions: stmt): stmt {.immediate.} =
|
||||
block:
|
||||
var f: TFile
|
||||
if open(f, fn, mode):
|
||||
try:
|
||||
actions
|
||||
finally:
|
||||
close(f)
|
||||
else:
|
||||
quit("cannot open: " & fn)
|
||||
var f: TFile
|
||||
if open(f, fn, mode):
|
||||
try:
|
||||
actions
|
||||
finally:
|
||||
close(f)
|
||||
else:
|
||||
quit("cannot open: " & fn)
|
||||
|
||||
withFile(txt, "ttempl3.txt", fmWrite):
|
||||
txt.writeln("line 1")
|
||||
txt.writeln("line 2")
|
||||
|
||||
In the example the two ``writeln`` statements are bound to the ``actions``
|
||||
parameter.
|
||||
parameter.
|
||||
|
||||
**Note:** The symbol binding rules for templates might change!
|
||||
|
||||
Symbol binding within templates happens after template instantiation:
|
||||
Symbol binding in templates
|
||||
---------------------------
|
||||
|
||||
A template is a `hygienic`:idx: macro and so opens a new scope. Most symbols are
|
||||
bound from the definition scope of the template:
|
||||
|
||||
.. code-block:: nimrod
|
||||
# Module A
|
||||
@@ -3572,34 +3582,11 @@ Symbol binding within templates happens after template instantiation:
|
||||
# Module B
|
||||
import A
|
||||
|
||||
echo genId() # Error: undeclared identifier: 'lastId'
|
||||
echo genId() # Works as 'lastId' has been bound in 'genId's defining scope
|
||||
|
||||
As in generics symbol binding can be influenced via ``mixin`` or ``bind``
|
||||
statements.
|
||||
|
||||
Bind statement
|
||||
--------------
|
||||
|
||||
Exporting a template is a often a leaky abstraction as it can depend on
|
||||
symbols that are not visible from a client module. However, to compensate for
|
||||
this case, a `bind`:idx: statement can be used: It declares all identifiers
|
||||
that should be bound early (i.e. when the template is parsed):
|
||||
|
||||
.. code-block:: nimrod
|
||||
# Module A
|
||||
var
|
||||
lastId = 0
|
||||
|
||||
template genId*: expr =
|
||||
bind lastId
|
||||
inc(lastId)
|
||||
lastId
|
||||
|
||||
.. code-block:: nimrod
|
||||
# Module B
|
||||
import A
|
||||
|
||||
echo genId() # Works
|
||||
|
||||
A ``bind`` statement can also be used in generics for the same purpose.
|
||||
|
||||
|
||||
Identifier construction
|
||||
@@ -3942,13 +3929,13 @@ Static params can also appear in the signatures of generic types:
|
||||
|
||||
type
|
||||
Matrix[M,N: static[int]; T: Number] = array[0..(M*N - 1), T]
|
||||
# Please, 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
|
||||
|
||||
AffineTransform2D[T] = Matrix[3, 3, T]
|
||||
AffineTransform3D[T] = Matrix[4, 4, T]
|
||||
|
||||
AffineTransform3D[float] # OK
|
||||
AffineTransform3D[float] # OK
|
||||
AffineTransform2D[string] # Error, `string` is not a `Number`
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user