Search code examples
templatesnim-langdo-notation

do notation magically fixes an otherwise refused expression as argument


In Nim templates: as a follow-up from this problem, I had the idea of working around the default-argument unavailability by using overloading, or even in this case, plain wrapping. Though, it would be too good if something didn't hit the fan again, let me share:

Notice that bodyFinally is now a hard (must specify) argument.

template tpl(x: bool, body: untyped, bodyFinally: untyped): void =
  if x: body
  else: bodyFinally

# we add a convenience helper with 2 args here.
template tpl2(x: bool, body: untyped): void =
  tpl(x) do:
    body
  do:
    discard

#call site:
var r: int
tpl2(true) do:
  r = 2

cool (it works). Though this was not my first shot for tpl2; this was:

template tpl2(x: bool, body: untyped): void =
  tpl(x, body, discard)

Because that's what do supposedly rewrites the thing anyway. Except we get:

Error: expression expected, but found 'keyword discard'

So what's up with that ?


Solution

  • It's a bit complicated to explain why, but you could have written the overload like this:

    template tpl(x: bool, body: untyped, bodyFinally: untyped) =
      if x: body
      else: bodyFinally
    
    template tpl(x: bool, body: untyped): void =
      tpl(x, body, (discard))
    
    var r = 1
    
    tpl(false):
      r = 2
    
    echo r
    
    tpl(true):
      r = 3
    
    echo r
    

    The extra brackets trigger a special parsing rule producing nkStmtListExpr AST node, which is a valid input for the template here. This construct is usually used in C-style if statements that include an assignment followed by a NULL-test:

    if (let f = fopen(...); f != 0):
      # do something with `f`
    

    As expected, the output of running the above program will be:

    1
    3