Passing a code block to a template

One can pass a block of statements as the last argument to a template following the special : syntax:

  1. template withFile(f, fn, mode, actions: untyped): untyped =
  2. var f: File
  3. if open(f, fn, mode):
  4. try:
  5. actions
  6. finally:
  7. close(f)
  8. else:
  9. quit("cannot open: " & fn)
  10. withFile(txt, "ttempl3.txt", fmWrite): # special colon
  11. txt.writeLine("line 1")
  12. txt.writeLine("line 2")

In the example, the two writeLine statements are bound to the actions parameter.

Usually to pass a block of code to a template the parameter that accepts the block needs to be of type untyped. Because symbol lookups are then delayed until template instantiation time:

  1. template t(body: typed) =
  2. proc p = echo "hey"
  3. block:
  4. body
  5. t:
  6. p() # fails with 'undeclared identifier: p'

The above code fails with the error message that p is not declared. The reason for this is that the p() body is type-checked before getting passed to the body parameter and type checking in Nim implies symbol lookups. The same code works with untyped as the passed body is not required to be type-checked:

  1. template t(body: untyped) =
  2. proc p = echo "hey"
  3. block:
  4. body
  5. t:
  6. p() # compiles