Re: a proc returning void creates 1 arg, not 0: breaking generic code
@Dennis my bad I should've verified the D code I posted; with tuples though we have: import std.stdio:writeln; void fun(T...)(T n) { static foreach (x; n) writeln(x); writeln("L=", T.length); } void main(){ import std.typecons:tuple; fun(tuple(1,2).expand); // L=2 fun(tuple(1).expand); // L=1 fun(tuple().expand); // L=0 } in any case, the previous answers make total sense regarding Nim's behavior.
Re: a proc returning void creates 1 arg, not 0: breaking generic code
> in D, variadic argument size will be 0 when calling fun(bar()); since bar > returns void isn't that a better behavior? if not what would be downsides of > that? It doesn't? Aside from the wrong semi-colon in the for-loop it gives an error: [https://run.dlang.io/is/6sfUA5](https://run.dlang.io/is/6sfUA5) "cannot deduce function from argument types !()(void)" It seems like weird behavior if that were the case. What would fun(1, bar(), 1); do, give a list of 2 integers?
Re: a proc returning void creates 1 arg, not 0: breaking generic code
> @timothee > > Why don't you write D as d? Please learn to write Nim with a capital letter. > This is disrespectful at least. I seriously doubt @timothee meant it in a disrespectful way. This is a programming forum, not a parliamentary chambers so no need to call people out for such things.
Re: a proc returning void creates 1 arg, not 0: breaking generic code
> fun(bar()) # n.len=1 ?? why not 0? Because fun is a macro, not a function. A macro call is not supposed to "evaluate" its arguments - definitely not untyped ones - before using them. It takes pieces of code, transforms them into AST nodes (in this case: one node, hence n.len = 1) and makes a new AST node out of them. Your D example shows a function, which is a completely different animal. Imagine bar had side effects. Would you really want fun(bar()) to result in code which does nothing (since the varargs argument would be considered "empty")?
Re: a proc returning void creates 1 arg, not 0: breaking generic code
> Maybe I'm missing some valid use case I haven't thought about? Nim macros cannot be directly compared to D templates/generics. You can use Nim macros to implement DSL, such as assembler, JIT engine, shader language, etc: macro my_asm_engine(n: varargs[untyped]): typed = bla bla bla my_asm_engine: mov eax, ebx xor eax, eax whatever if Nim macros choose to ignore void returning function call, it would be unusable to implement DSL at all.
Re: a proc returning void creates 1 arg, not 0: breaking generic code
@rpowers thanks, that solves my problem! and thanks for the rationale, makes sense.
Re: a proc returning void creates 1 arg, not 0: breaking generic code
@timothee Why don't you write D as d? Please learn to write Nim with a capital letter. This is disrespectful at least.
Re: a proc returning void creates 1 arg, not 0: breaking generic code
Oh, right! I guess it's testing it on untyped rather than the actual type in the macro. Anyways, this one is definitely working, it just tests for void-typed arguments: import macros import typetraits import strformat macro fun*(n: varargs[typed]): typed = result = newNimNode(nnkStmtList, n) var echoedCount = 0 for x in n: if x.getTypeInst.typeKind != ntyVoid: result.add(newCall("echo", x)) echoedCount += 1 result.add(newCall("echo", newStrLitNode($(echoedCount proc bar() = echo "ok" fun(1+1) # n.len=1 fun() # n.len=0 # comment this to make it work fun(bar()) > isn't that a better behavior? if not what would be downsides of that? No, I think automatically filtering void arguments in a macro is not a better or intuitive behavior. What if you remove a return type from a function and the compiler just silently starts ignoring it in argument lists instead of producing a type mismatch error? That seems like it could be an interesting source of bugs. I guess what you're asking for is not SFINAE, but it feels similar -- it's a hack used to make template systems have some limited metaprogramming. I think that you'll find that sort of thing is not necessary with Nim, you can just write a macro that does what you want. Is there something specific that you're trying to do that requires the argument-ignoring approach used in D? Maybe we could help you come up with a good solution for it in Nim.
Re: a proc returning void creates 1 arg, not 0: breaking generic code
> bar returns nothing, how could you echo it. I see nothing to fix here, > especially not the involved lengths. if nim treated a void-returning function call as being 0 arguments as in D (as if no argument was passed) then there would be no call to echo (ie, fun(bar()) would call bar() and then fun()) ; this introduces less edge cases (no need to consider void variables). Maybe I'm missing some valid use case I haven't thought about? > I guess that D has implemented some kind of SFINAE that won't run for args > that cause errors? no, it's simpler than that: as I was explaining above, in D, variadic argument size will be 0 when calling fun(bar()); since bar returns void isn't that a better behavior? if not what would be downsides of that? > it doesn't need those sorts of things, but you can certainly emulate the > behavior. your code doesn't work, compiles(echo(x)) still returns true; here's a full modified example so you can click run: import macros import typetraits import strformat macro fun*(n: varargs[untyped]): typed = result = newNimNode(nnkStmtList, n) var echoedCount = 0 for x in n: if compiles(echo(x)): result.add(newCall("echo", x)) echoedCount += 1 result.add(newCall("echo", newStrLitNode($(echoedCount proc bar() = echo "ok" fun(1+1) # n.len=1 fun() # n.len=0 # comment this to make it work fun(bar())
Re: a proc returning void creates 1 arg, not 0: breaking generic code
I guess that D has implemented some kind of SFINAE that won't run for args that cause errors? Since Nim has a full macro system, it doesn't need those sorts of things, but you can certainly emulate the behavior. Use the proc compiles, which will return true if an expression compiles without errors: macro fun*(n: varargs[untyped]): typed = result = newNimNode(nnkStmtList, n) var echoedCount = 0 for x in n: if compiles(echo(x)): result.add(newCall("echo", x)) echoedCount += 1 result.add(newCall("echo", newStrLitNode($(echoedCount proc bar() = echo "ok"
Re: a proc returning void creates 1 arg, not 0: breaking generic code
`bar` returns nothing, how could you `echo` it. I see nothing to fix here, especially not the involved lengths.
a proc returning void creates 1 arg, not 0: breaking generic code
import macros import typetraits import strformat macro fun*(n: varargs[untyped]): typed = result = newNimNode(nnkStmtList, n) for x in n: result.add(newCall("echo", x)) result.add(newCall("echo", newStrLitNode($(n.len # void-return proc proc bar() = echo "ok" fun(1+1, "abc", 42) # n.len=3 fun(1+1, "abc") # n.len=2 fun(1+1) # n.len=1 fun() # n.len=0 # gives Error: type mismatch: got fun(bar()) # n.len=1 ?? why not 0? in D it works correctly and doesn't break generic code: void fun(T...)(T n){ static foreach(x:n) writeln(x); writeln(T.length); } void bar(){writeln("ok");} fun(1+1, "abc", 42); fun(1+1, "abc"); fun(1+1); fun(); fun(bar()); * is that nim choice by design or could that be fixed so that n.len = 0 in above code? * how would we work around it in my macro fun to detect whether the type of x is void and proceed accordingly?