I was hacking around with trying to use templates to combine inline iterators, and I wasn't successful, but I'm not sure what the problem is.
template chain(iter1, iter2: typed): typed =
iterator res: string {.inline.} =
for r1 in iter1():
yield r1
for r2 in iter2():
yield r2
res
iterator thing1: string =
yield "A"
yield "B"
yield "C"
yield "D"
iterator thing2: string =
yield "E"
yield "F"
yield "cookiemonster"
for thing in chain(thing1,thing2):
echo(thing)
The error is "value of type 'iterator (): string{.inline, noSideEffect, gcsafe, locks: 0.}' has to be discarded; for a function call use ()"
It's pretty easy to make something like this work:
chain(dummyname,thing1,thing2)
for thing in dummyname:
...
But I can't quite get the template to produce something that goes in the for statement itself.
The other problem is the iterators might not always yield a string, but it eludes me how to declare my wrapper iterator to return type(elementof(thing1))
This is what might happen:
for thing in
iterator res: string {.inline.} =
for r1 in iter1():
yield r1
for r2 in iter2():
yield r2
This is what you actually need:
template chain(iter1, iter2: typed, body: untyped) =
block:
for str {.inject.} in iter1():
body
for str {.inject.} in iter2():
body
iterator thing1: string =
yield "A"
yield "B"
yield "C"
yield "D"
iterator thing2: string =
yield "E"
yield "F"
yield "cookiemonster"
chain(thing1,thing2):
echo(str)
Run itFor your second question, either you use 'case objects' or plain when blocks:
template chain(iter1, iter2: typed, body: untyped) =
block:
for v {.inject.} in iter1():
body
for v {.inject.} in iter2():
body
iterator thing1: string =
yield "A"
yield "B"
iterator thing2: int =
yield 1
yield 2
chain(thing1,thing2):
when v is string:
echo("String: ", v)
elif v is int:
echo(v + 5)
That's good information, I suppose. Still doesn't let me chain iterators in a "for" statement, leaving people having to examine my code to figure out what I mean by "chain(a,b)" It also relies on magic identifiers like "str" and "v". I ended up messing around with macros to get the desired behavior, which is pretty silly, but here you go:
import macros
macro chaining(code: untyped): untyped =
const chainIdent = "chain"
const combineIdent = "combine"
proc inspect(depth: int, n: NimNode): NimNode =
case(n.kind)
of nnkIdent, nnkStrLit:
return n;
of nnkForStmt:
let onevar = n.len == 3
let call = n[n.len-2]
if call.kind == nnkCall:
proc get_iter(j: int): NimNode =
let item = call[j]
if item.kind == nnkIdent:
return newCall(item)
item
case($call[0].ident)
of chainIdent:
proc get_name(): NimNode =
if onevar:
return n[0]
result = newNimNode(nnkPar)
for i in 0..n.len - 2:
result.add(n[i])
let name = get_name()
let body = inspect(depth+1,n[n.len-1])
let list = newStmtList()
for j in 1..call.len-1:
list.add(newTree(nnkForStmt,name,get_iter(j),body.copy()))
# echo(treeRepr(list))
return newBlockStmt(list)
of combineIdent:
var body = inspect(depth+1,n[n.len-1])
var par = newNimNode(nnkPar)
for j in 1..call.len-1:
par.add(genSym(nskForVar))
var vars: NimNode
if onevar:
vars = newNimNode(nnkIdentDefs)
vars.add(n[0])
else:
vars = newNimNode(nnkVarTuple)
for i in 0..n.len - 3:
vars.add(n[i])
vars.add(newEmptyNode())
vars.add(par)
body.insert(0,newTree(nnkLetSection,vars))
for j in 1..call.len-1:
let iter = get_iter(j)
let sym = par[j-1]
let f = newTree(nnkForStmt,sym,iter,body)
body = f
# echo(treeRepr(body))
return body
else: discard
else: discard
var children: seq[NimNode] = @[]
for i in 0..n.len-1:
children.add(inspect(depth+1,n[i]))
result = newNimNode(n.kind)
if children.len > 0:
result.add(children)
result = inspect(0,code)
# echo(treeRepr(result))
iterator thing1: string =
yield "A"
yield "B"
yield "C"
iterator thing2: string =
yield "D"
yield "E"
yield "F"
yield "cookiemonster"
iterator thing3(i: int): int =
yield 1+i
yield 2+i
yield 3+i
chaining:
echo("regular iteration.......")
for thing in thing1():
echo(thing)
echo("chaining.......")
for thing in chain(thing1,thing2,["wait, try that again"],thing1,thing3(0),"derp"):
echo(thing)
echo("combinations.......")
for a,b in combine(thing1,thing3(39)):
echo(a, " ", b)
echo(".......")
for a,b in combine(thing1,thing1):
echo(a, " ", b)
echo("can capture as a tuple-ish thing.......")
for thing in combine(thing1,thing3(23)):
echo(thing)
let (a,b) = thing
echo(a," ",b)