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)