The problem is that iterators default to being **inline**. This means the
compiler transforms the iterator into a block of code that is executed **only
when it is in an iteration construct** like a for loop.
So in
for file in walkDir(datapath / source / $instrument / "Prices"):
paths.add(file.path)
...
Run
This would actually be transformed into (I believe) something like this:
...
when nimvm:
for k, v in items(staticWalkDir(datapath / source / $instrument /
"Prices", relative)):
let file: tuple[kind: PathComponent, path: string] = (k, v)
paths.add(file.path)
...
...
Run
So then the compiler isn't actually _calling_ anything per se, but detecting
when an iterator is used in a for loop and then making the transformation
_inline_.
When using the `{.closure.}` pragma, the compiler will make the iterator an
actual function call (or something similar), making the iterator symbol a so
called "first class citizen" that can be used how you are wanting to in your
second example.
To get it to work in your first example, you can return a closure iterator from
a pricestream proc like so:
import os
import algorithm
type
Instrument = object
name: string
Price = float
proc `$`(ins: Instrument): string =
ins.name
var datapath = "."
proc pricesFromCsv(path: string): seq[Price] =
# Just hardcoded for example
return @[1.0, 2.0, 3.0, 4.0, 5.0]
proc pricestream*(source: string, instrument: Instrument): iterator():
Price =
# When returning an iterator type, {.closure.} is inferred.
return iterator(): Price =
## Iterator for all historic prices for the source, and instrument.
var paths: seq[string]
for file in walkDir(datapath / source / $instrument / "Prices"):
paths.add(file.path)
paths.sort(system.cmp[string])
for path in paths:
for price in pricesFromCsv(path):
yield price
var EUR_USD = Instrument(name: "EUR_USD")
var stream = pricestream("oanda", EUR_USD)
for p in stream(): # brackets are needed here to invoke the iterator
echo p
Run