Hey, I want to use an iterator as argument for a function. I know, that iterators cant be passed around, and they need to be wrapped in functions. However, I cant get it to work. I something like this:
import tables, sugar
type Iterable[T] = proc: iterator: T
proc iterSomething(xs: Iterable[int])=
for x in xs()():
echo x
let dict = toTable {1:2, 3:4}
iterSomething(() => dict.values)
which will produce the following error:
Error: undeclared field: 'values'
found 'tables.values(t: OrderedTableRef[values.A, values.B]) [declared in /home/felix/.choosenim/toolchains/nim-1.4.0/lib/pure/collections/tables.nim(2118, 10)]' of kind 'iterator'
found 'tables.values(t: CountTable[values.A]) [declared in /home/felix/.choosenim/toolchains/nim-1.4.0/lib/pure/collections/tables.nim(2529, 10)]' of kind 'iterator'
found 'tables.values(t: OrderedTable[values.A, values.B]) [declared in /home/felix/.choosenim/toolchains/nim-1.4.0/lib/pure/collections/tables.nim(1710, 10)]' of kind 'iterator'
found 'tables.values(t: CountTableRef[values.A]) [declared in /home/felix/.choosenim/toolchains/nim-1.4.0/lib/pure/collections/tables.nim(2805, 10)]' of kind 'iterator'
found 'tables.values(t: Table[values.A, values.B]) [declared in /home/felix/.choosenim/toolchains/nim-1.4.0/lib/pure/collections/tables.nim(708, 10)]' of kind 'iterator'
found 'tables.values(t: TableRef[values.A, values.B]) [declared in /home/felix/.choosenim/toolchains/nim-1.4.0/lib/pure/collections/tables.nim(1136, 10)]' of kind 'iterator'
I don't really understand how this error comes to be. How can I pass arbitrary iterators without converting them to a sequence?
By wrapping them in a .closure iterator.
import tables, sugar
type Iterable[T] = iterator: T
proc iterSomething(xs: Iterable[int])=
for x in xs():
echo x
template toFirstClassIter(x): untyped =
iterator it(): auto {.closure.} =
for y in x: yield y
it
let dict = toTable {1:2, 3:4}
iterSomething(toFirstClassIter dict.values)
By wrapping them in a .closure iterator.
closure iterators have limitations (no js, no vm, no nimscript, and have performance overhead)
Once again, https://github.com/nim-lang/Nim/pull/11992 (every symbol becomes 1st class; defines 0-cost lambda and aliases; inline iterators/templates/etc can be passed to any routine ) would enable exactly this, among many other things.
But this misses the point a little. I wanted to avoid passing around containers that would then get copied, and pass around iterators instead. In your examples ivec, fvec and dict would get copied, wouldn't they?
With the help of the Discord chat, I now ended up defining toFirstClassIter like this:
template toFirstClassIter*(it: untyped): untyped=
(iterator(): auto =
for x in it:
yield x)
and use zero-functional instead of sequtils. That works well for me so far.
I wanted to avoid passing around containers that would then get copied, and pass around iterators instead
This is exactly what @timothee's proposal is good for. It allows you to pass iterators directly. The compiler expands the iterator structurally via inlining, or, at least, it has the possibility to do so. Nothing gets copied here.
You wrapped the same thing, the iterator, in a template. @timothee's proposal works with templates and iterators as well. vecitems gets changed to toFirstClassIter , that's it. But there is a major difference here. You had to resort to untyped and auto. @timothee did the same in his running examples. This was @Araq's point.
So, i gave an example avoiding auto. The iterator and the template are isomorphic in the particular case. But this will not hold generally because of untyped. Type reasoning with typed templates/iterators/procs etc. is easier certainly.
I would not call it a hack as much as ill thought out, it cannot be used with a typeclass and does not automatically use pairs or items so sequences/arrays require manual .items.
It cannot be used as an inline iterator parameter, as you may know inline iterators are after all just fancy templates. Iterable is in my view a step in the right direction but only a minor implementation of what should be done. I will note there is an RFC for implicit iterators -> iterable, though there is no RFC for allowing it inside of an iterator parameter list.