Macros and procs are two vastly different tools. A procedure is for writing 
code that runs(at runtime or compile time). A macro is for writing code that 
expands code at compile time. Macros enable introspection and automation of 
writing code. Due to this you can do many magical things with macros, say you 
have a serialization library and want to only serialized fields of an object 
that are tagged, this can be done with a macro and a pragma. Macros should not 
be used in place of procedures, they should be used since procedures cannot be.

The following is a small example that is hopefully easily understandable to 
showcase what macros bring that procedures cannot do. Unpacking tuples/arrays 
into proc calls for less redundant code. 
    
    
    import std/macros
    
    macro `<-`(p: proc, args: tuple): untyped =
      ## Takes a proc and varargs, unpacking the varargs to the proc call if 
it's a tuple or array
      result = newCall(p) # Makes result `p()`
      echo args.treeRepr # Shows the input tuple
      for arg in args:
        let
          typ = arg.getType # Gets arg typ
          isSym = typ.kind == nnkSym
        echo typ.treeRepr # Shows the type we got
        if not isSym and typ[0].eqIdent("tuple"): # This is a tuple so unpack 
values from arg
          for i, _ in typ[0..^2]:
            result.add nnkBracketExpr.newTree(arg, newLit(i))
        elif not isSym and typ[0].eqIdent("array"): # This is a tuple so unpack 
values from arg
          for i in typ[1][1].intVal .. typ[1][2].intVal:
            result.add nnkBracketExpr.newTree(arg, newLit(i))
        else: # Otherwise we just dumbly add the value
          result.add arg
    
    
    proc doThing(a, b: int) = echo a, " ", b
    proc doOtherThing(a, b: int, c, d: float) =
      doThing <- (a, b)
      echo c, " ", d
    
    doThing <- (10, 20)
    doThing <- ([10, 20],)
    doThing <- ([10], (20,))
    
    doOtherThing <- ([10, 20], (30d, 40d))
    doOtherThing <- (10, 20, 30d, 40d)
    
    
    Run

Reply via email to