Consider this example:
iterator fibo(a, b: int): int {.closure.} =
var
a = a
b = b
while true:
yield a
(a, b) = (b, a+b)
let x = fibo
for _ in 1 .. 5:
echo x(1, 1)
echo x(10, 20)
echo x(100, 999)
echo "\n##########\n"
let
y = fibo
z = y(5, 8)
for i in 1 .. 5:
echo z
In the first case, once you start the iterator, passed values don't matter. Why then even allow it?
In the second case, I thought about passing the values just the first time when defining z and then just call it, to reduce the confusion. Can't do.
For me, the expected syntax/behaviour would be something like this:
let c = fibo(1, 1)
for i in 1 .. 5:
echo next(c)
What do you guys think? Is this strange/confusing just for me?
Can the above example with next (or something similar to it) somehow be implemented in current version of Nim? (please don't just answer with "use macros" :D I have no experience with them - an example how to do it would be great)
You don't need macros at all.
Here is what you really wanted to do:
proc fibo(a, b: int): iterator: int =
iterator inner: int =
var
a = a
b = b
while true:
yield a
(a, b) = (b, a+b)
inner
let x = fibo
for _ in 1 .. 5:
echo x(1, 1)()
echo x(10, 20)()
echo x(100, 999)()
echo "\n##########\n"
let
y = fibo
z = y(5, 8)
for i in 1 .. 5:
echo z()
The problem you encountered is the bizarre semantics of assignment of iterators. In fact, let x = fibo actually created a new object of iterator. When it passed through var a = a; var b = b, changing parameters a and b won't change anything. You can check it does if you don't shadow the arguments:
iterator fibo(a, b: int): int {.closure.} =
var
c = a
d = b
while true:
yield a
(c, d) = (b, a+b)
let x = fibo
for _ in 1 .. 5:
echo x(1, 1)
echo x(10, 20) # here the result is different
echo x(100, 999)
All in all: an iterator is an object. When you "call" it, you change it's fields' (arguments') values and resume its the execution from last yield to the next one. If you wanted a NEW object when you call with different arguments, what you should return is an iterator.
The problem with Nim's iterator is that sometimes "iterator" means a procedure which creates a new iterator (the one you declare with "iterator" keyword), sometimes an object the said procedure returns and sometimes both inlined into a loop.
Your second example always return 1 in a loop, because it yields a.
Your output is:
1
1
1
1
1
10
100
and one could think new iterators have been invoked (with the starting values of a = 10 and 100, respectively).
Changing to yield c gives:
1
1
1
1
1
20
999
Now it is clear that (in both cases) iterator just continues where it has stopped, there was no new iterators!
But the syntax (to me) really feels/looks like a new iterator (with new starting values) was called. I can't shake the feeling about wrong-looking syntax.
Your first example with proc slightly remedies this.
There is system.finished which can be used with iterators. I still think some kind of system.next would be of great help with readability/usability of iterators.
There is system.finished which can be used with iterators. I still think some kind of system.next would be of great help with readability/usability of iterators.
I do sort of agree. Maybe you could ask @Araq what he thinks about this :)
Maybe you could ask @Araq what he thinks about this
I just did on IRC and he confirms he is not satisfied with the current implementation.
I created a Github issue.