templatesnim-lang

Why is my call of a nim template not simply expanding the template code?


Working in Nim, my intuition for templates is that, at compile time, they are simply expanded into their code (with the appropriate substitutions). But the following code:

type
    New_Type = ref object of RootObj

template Template_Procedure(T: untyped, vProc: bool = true) = 

    proc ProcedureA(self:var T, vInt: var int): int 

    when vProc == true:
        proc ProcedureA(self:var T, vInt: var int): int =
            var tReturn = vInt * vInt 
            return tReturn

    proc ProcedureB(self: var T, vInt: var int): int = 
        var tI = ProcedureA(self, vInt)
        return 2 * tI 

Template_Procedure(New_Type, vProc = false)

proc ProcedureA(self:var New_Type, vInt: var int): int =
    var tReturn = vInt * vInt * vInt
    return tReturn

var tNew = new(New_Type)
var tInt = 5
echo $ProcedureB(tNew, tInt)    

gives the error:

test3.nim(9, 10) Error: implementation of 'test3.ProcedureA(self`gensym0: var New_Type, vInt`gensym0: var int)' expected

If however I instead call

Template_Procedure(New_Type, vProc = true)

then there are no errors.

In contrast, if I manually substitute the text of the template in for the place where it is called, then the opposite occurs. I.e. the code compiles if I substitute false for vProc and I get the error

Error: redefinition of 'ProcedureA'; previous declaration

if I substitute true for vProc.

Can someone explain to me where my intuition is incorrect regarding templates? (or give me appropriate references)

Also, is there a way to write code which at compile time is simply expanded?

Specifically I would like to be able to call Template_Procedure(X, vProc = false) where X is a type and then, for appropriate types X, manually write specific

ProcedureA(self: var X, vInt var int): int

procedures. And for other X simply call Template_Procedure(X) and not have to manually define ProcedureA.


Solution

  • The forward declare never gets an implementation as templates are hygienic. Although they both appear to have the name ProcedureA since you are inside template space they are not named the same. You likely should mark the template {.dirty.} or use {.inject.} on the procedures to inject the symbol names.

    To the following question I do not know what you mean, templates are simply expanded.