-
Notifications
You must be signed in to change notification settings - Fork 23
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[RFC] Better iterators syntax #1
Comments
Related to: nim-lang/Nim#2563 What about |
IIRC |
This sounds like something that returns In the example above, |
I wrote a marco that allows a different syntax, what do you think about it. Be aware it's a proof of concept, not something final you want to work with. For example I broke the possibility to use this iterator in a simple for loop. Only the import macros
macro instanceIterator(iter: iterator, args: varargs[typed]): untyped =
let formalParams = iter.getTypeInst[0]
let tupleTy = nnkTupleTy.newTree
let argsSym = genSym(nskVar, "tmp")
let assignments = nnkPar.newTree()
var argi = 0
for i, node in formalParams:
if i == 0:
continue
else:
let identDefs = nnkIdentDefs.newTree
for j, n in node:
if j < node.len-2:
let l = genSym(nskField, $n)
identDefs.add l
assignments.add( nnkExprColonExpr.newTree( l , args[argi] ) )
argi += 1
else:
identDefs.add n
tupleTy.add identDefs
result = quote do:
var `argsSym`: `tupleTy` = `assignments`
(it: `iter`, args: `argsSym`)
proc getTupleLength(arg: NimNode): int {.compileTime.} =
let typ = arg.getTypeInst
result = 0
typ.expectKind nnkTupleTy
for identDefs in typ:
identDefs.expectKind nnkIdentDefs
result += identDefs.len - 2
macro forwardTupleAsArgs(call, args: typed): untyped =
## a function symbol `call` and a tuple symbol `args` will be
## transformed into `call(args[0], args[1], ...)`
result = newCall(call)
let numArgs = getTupleLength(args)
for i in 0 ..< numArgs:
result.add nnkBracketExpr.newTree(args, newLit(i))
import options
proc next[T: tuple](arg: T): Option[int] =
let value = forwardTupleAsArgs(arg.it, arg.args)
if not finished(arg.it):
result = some(value)
macro newClosure(arg: untyped): untyped =
## This macro replaces the identifier of the iterator with a hidden
## symbol and uses the identifier wrappring procedure. The wrapping
## procedure creades an iterator object.
let iteratorDef =
if arg.kind == nnkStmtList:
arg.expectLen 1
arg[0]
else:
arg
iteratorDef.expectKind nnkIteratorDef
let newSym = genSym(nskIterator, $iteratorDef[0] & "Hidden")
let oldSym = iteratorDef[0]
iteratorDef[0] = newSym
iteratorDef[4] = nnkPragma.newTree(
newIdentNode("closure")
)
let formalParams = iteratorDef[3].copyNimTree
formalParams.expectKind nnkFormalParams
formalParams[0] = ident"untyped"
let call = newCall( bindSym"instanceIterator", newSym )
for i, identDefs in formalParams:
if i == 0:
continue
else:
identDefs.expectKind nnkIdentDefs
for i in 0 ..< identDefs.len - 2:
call.add ident($identDefs[i])
let templateDef = nnkTemplateDef.newTree(
oldSym, newEmptyNode(), newEmptyNode(),
formalParams.copyNimTree, newEmptyNode(), newEmptyNode(),
newStmtList( call )
)
result = newStmtList(
iteratorDef,
templateDef
)
################################################################################
# from here is example code
echo "\nexample1:"
block example1:
iterator fibo(a, b: int): int {.newClosure.} =
var
a = a
b = b
while true:
yield a
(a, b) = (b, a + b)
let it = fibo(1, 1)
echo " ", it.next
for _ in 1 .. 5:
echo " ", it.next
echo " ", it.next
echo " ", it.next
echo "\nexample2:"
block example2:
iterator fiboFinite(a, b, c: int): int {.newClosure.} =
var
a = a
b = b
for _ in 0 ..< c:
yield a
(a, b) = (b, a + b)
let it2 = fiboFinite(0,1,4)
echo " ", it2.next
for _ in 1 .. 5:
echo " ", it2.next
echo " ", it2.next
echo " ", it2.next This is the output:
EDIT: |
What about
? For me it's solving the practical problem of cleanly advancing multiple iterators in a |
@nellore good that it solves your purpose. But it is definitively not a general case solution, because it just doesn't compile on anything that is not a |
Oh, I see! Thanks. |
BUMP |
@narimiran well you can give feedback on the macro or improve on it. For me it does exactly what you are asking for and it solves the problem to see if an iterator has ended in a clean and obvious way. |
I would gladly, but I can do neither of those two - I have zero experience with macros and I don't even understand the code you have written :) Maybe somebody more experienced than me can give comment on your solution? |
Well, you can give me feedback about the example part of the code. You don't need to comment on the macro implementation itself, just if you can do something with the result of it. |
@krux02 The macro is nice, but without being able to use the iterator in a normal for loop it is not a general solution. The other reason is:
What bothers me the most is the following inconsistency: proc iterGen(someEvalOnceArg: int): (iterator (): int {.closure.}) =
result = iterator (): int =
yield 1
yield 2
yield 3
# this works fine
let it = iterGen(42)
for x in it():
echo x
# this is an infinite loop
for x in iterGen(42)():
echo x I run into this infinite loop a lot, because the code intuitively should be equivalent. I was hoping that this RFC also addresses this. I see a possibility that if iterators would use |
This RFC is stale because it has been open for 1095 days with no activity. Contribute a fix or comment on the issue, or it will be closed in 30 days. |
Problem
Iterator syntax as currently implemented leaves a lot to be desired.
As seen in this forum post, there are couple of problems with current syntax:
let x = fibo
(without()
and without parameters)(1, 1)
at the first call) or might not ((10, 20)
,(100, 999)
) have anything to do with the current iteratorProposal:
Use
next
to call the existing iterator. (Similar to other languages, e.g. Python)The text was updated successfully, but these errors were encountered: