I need to define the pairs iterator from a template, because the template brings some global symbols, something like:
type
Melon = object
template pairs*(m: Melon): tuple[key: int, val: int] =
iterator pairs(m: Melon): tuple[key: int, val: int] =
for i in 0 .. 10:
yield (key: i, val: i * i)
let m = Melon()
for k, v in m:
echo "k=", k, "; v=", v
This code does not compile with the cryptic error message toto.nim(10, 13) Error: cannot use symbol of kind 'let' as a 'param'. nim check adds a few more hints:
Error: expression has no type: iterator pairs(m: Melon): tuple[key: int, val: int] =
for i`gensym4552018 in 0 .. 10:
yield (key: i`gensym4552018, val: i`gensym4552018 * i`gensym4552018)
toto.nim(5, 12) Hint: 'pairs' is declared but not used [XDeclaredButNotUsed]
Could it be because the template and the iterator have the same name? But I need the template to be named pairs to be implicitly called by the compiler, no?
Changing m to be a var changes the error message to toto.nim(10, 13) Error: cannot use symbol of kind 'var' as a 'param' but it does not make the error more understandable.
So is it possible to write an implicite pairs iterator from a template?
The global symbol is in fact a context parameter that is used to call various API. Instead of passing it as a parameter to all templates, it is defined in a macro and then used by all templates. I've tried to replicate in my small example with the value a that I used in the loop.
type
Melon = object
template pairs*(m: Melon): tuple[key: int, val: int] =
iterator pairs(m: Melon): tuple[key: int, val: int] =
for i in a .. 10:
yield (key: i, val: i * i)
block:
let a = 2
var m = Melon()
for k, v in m:
echo "k=", k, "; v=", v
Ah, in that case, you can use an inline closure iterator with no name:
type
Melon = object
template pairs*(m: Melon): tuple[key: int, val: int] =
let iter = iterator(melon: Melon): tuple[key: int, val: int] =
for i in a .. 10:
yield (key: i, val: i * i)
iter(m)
block:
let a = 2
let m = Melon()
for k, v in m:
echo "k=", k, "; v=", v
The toto.nim(10, 13) Error: cannot use symbol of kind 'let' as a 'param' error you were experiencing was because you used m as the name for the template argument AND for the iterator argument. Templates will expand argument names to something like m`gensym4552018, so you need to name the argument of your inner iterator something different. In the code above, I simply renamed it to melon.
You could also do it without a closure iterator like so:
type
Melon = object
template pairs*(m: Melon): tuple[key: int, val: int] =
(iterator(melon: Melon): tuple[key: int, val: int] =
for i in a .. 10:
yield (key: i, val: i * i))(m)
block:
let a = 2
let m = Melon()
for k, v in m:
echo "k=", k, "; v=", v
But IMO, looks a bit messier.
The toto.nim(10, 13) Error: cannot use symbol of kind 'let' as a 'param' error you were experiencing was because you used m as the name for the template parameter AND for the iterator parameter. Templates will expand input parameter names to something like m`gensym4552018, so you need to name the parameter of your inner iterator something different. In the code above, I simply renamed it to melon.
That's the second time I've got bitten by that and I even wrote it in the Nim wiki as a vicious cause of bugs with templates... I think I'll add it a second time in that page.
But even with your code using the anonymous iterator, I still can't compile it, now with a different error message Error: ignored invalid for loop.
I've setup a playground version to be able to try different Nim versions.
I think that when I pause on my current project, I'll dedicate some time to understand Nim compiler and find a way to improve error messages. I spend so much time trying to understand what they mean and where they apply...
From what I understand from the source of the error, now the call to the iterator is no more considered to be in a for loop.
Again nim check accepts the code while it does not compile.
Thanks a thousands for the work around!
I saw it working in the playground and I reported it in my project, but I still got the same error. Then I reminded that I had put doc comments in my template. Bingo! That was the reason again for this error.
And in order to keep the doc comments, the issue shows that strangely
template pairs*(m: Melon): tuple[key: int, val: int] = ##
## Works
(iterator(melon: Melon): tuple[key: int, val: int] =
...
while
template pairs*(m: Melon): tuple[key: int, val: int] =
## Does not work! ==> Error: ignored invalid for loop
(iterator(melon: Melon): tuple[key: int, val: int] =
...
doesn't!
Nim parser accepts a very flexible syntax that makes the language extensible but is full of mystery.