I have been trying to figure out how to get this working for the last few days, but no luck yet. Consider this snippet:
template skel(T: untyped): untyped =
let fn = proc(): T =
echo "I am fn"
fn
proc gen(): NimNode =
result = getAst skel(int)
macro foo(): untyped =
gen()
let f = foo()
This is the bare minimum I can reduce my real life code to. The macro foo() calls proc gen() to generate code. gen() runs getAst() on template skel() which contains the code that should be generated.
The code is organized like this for good reasons: in my real code gen() generates a bit more code which gets injected in the skel() template, so I think I can not easily make big structural changes.
The above works, but I am looking for a way to get the type T all the way out as a parameter the foo() macro. Ideally, this is what I would like to do, although it does not compile:
template skel(T: untyped): untyped =
let fn = proc(): T =
echo "I am fn"
fn
proc gen[T](): NimNode =
result = getAst skel(T) # <-- error: Error: getAst takes a call, but got getAst
macro foo(T: typedesc): untyped =
gen[T]()
let fn = foo(int)
I do not understand what Nim is trying to say here getAst takes a call but got getAst? So let's pass the type as an argument:
template skel(T: untyped): untyped =
let fn = proc(): T =
echo "I am fn"
fn
proc gen(T: typedesc): NimNode =
result = getAst skel(T)
macro foo(T: typedesc): untyped =
gen(T) # <-- Error: type mismatch: got <NimNode>
let fn = foo(int)
Nope, doesn't work - the typedesc argument of the macro gets converted to a NimNode and I can't figure out how to turn it back into a typedesc
I've tried numerous permutations resulting in numerous other interesting error messages, I went through the manual a number of times and I discussed this on #nim, but I am still lost. How to solve this?
Thanks!
@kaushalmodi: also tried the static variants, no luck
@jasper: No reason other then I just happened to write down these two of the numerous posibillities I have tried - proc gen(T: NimNode): NimNode also doesn't work.
Have I hit some bug?
Well, do I feel stupid now :)
Thanks!
So, now I ended up with this (thanks Jasper):
template skel(T: untyped): untyped =
let fn = proc(): T =
echo "I am fn"
fn
proc gen(T: NimNode): NimNode =
getAst skel(T)
macro foo(T: typedesc): untyped =
gen(T)
discard foo(int)()
I know there are very good reasons why things work the way they do, but the above is not obvious, trivial or simple. There is a thing called generics, but you can't use it for macros, and all macro arguments are NimNodes, except for typedescs. The generics/Nim-idiomatic way to do this would be something like this:
template skel[T](): untyped =
let fn = proc(): T =
echo "I am fn"
fn
proc gen[T](): NimNode =
getAst skel[T]()
macro foo[T](): untyped =
gen[T]()
discard foo[int]()()
Maybe one day in Nim 2.0 :)