I'm trying to create a View type that lazily applies a callback when iterating over a set of values. My current implementation uses an iterator closure, but those don't work in JS.
I've tried to create a reduced example of what I'm trying to accomplish:
type
Database*[R] = object
## A bunch of arbitrary data
rows: seq[R]
View*[V] = object
## A view into the data above, but mapped just-in-time
iter: proc(): iterator(): V
proc createView*[R, V](db: Database[R], mapper: proc(row: R): V): View[V] =
result.iter = proc(): auto =
return iterator(): V {.closure.} =
for row in db.rows:
yield mapper(row)
iterator items*[V](view: View[V]): V =
let instance = view.iter()
for item in instance():
yield item
## -------------------------
## Another file
var foo: Database[(int, int)]
foo.rows.add((1, 2))
foo.rows.add((3, 4))
let view = foo.createView(proc (row: (int, int)): auto = ($row[0], $row[1]))
for (a, b) in view:
echo a, " ", b
https://play.nim-lang.org/#ix=4kgR
What strategy do you recommend for implementing something like that such that it would work with the JS target?
Could use an iterator pattern, this is what languages without iterators do (but using objects instead of closure variables)
import options
type
Database*[R] = object
## A bunch of arbitrary data
rows: seq[R]
View*[V] = object
## A view into the data above, but mapped just-in-time
iter: proc(): proc(): Option[V]
proc createView*[R, V](db: Database[R], mapper: proc(row: R): V): View[V] =
result.iter = proc(): auto =
var i = 0
return proc(): Option[V] =
if i < db.rows.len:
let row = db.rows[i]
inc i
return some(mapper(row))
else:
return none(V)
iterator items*[V](view: View[V]): V =
let instance = view.iter()
while (let item = instance(); item.isSome):
yield item.unsafeGet
## -------------------------
## Another file
var foo: Database[(int, int)]
foo.rows.add((1, 2))
foo.rows.add((3, 4))
let view = foo.createView(proc (row: (int, int)): auto = ($row[0], $row[1]))
for (a, b) in view:
echo a, " ", b