Order of evaluation

Order of evaluation is strictly left-to-right, inside-out as it is typical for most others imperative programming languages:

  1. var s = ""
  2. proc p(arg: int): int =
  3. s.add $arg
  4. result = arg
  5. discard p(p(1) + p(2))
  6. doAssert s == "123"

Assignments are not special, the left-hand-side expression is evaluated before the right-hand side:

  1. var v = 0
  2. proc getI(): int =
  3. result = v
  4. inc v
  5. var a, b: array[0..2, int]
  6. proc someCopy(a: var int; b: int) = a = b
  7. a[getI()] = getI()
  8. doAssert a == [1, 0, 0]
  9. v = 0
  10. someCopy(b[getI()], getI())
  11. doAssert b == [1, 0, 0]

Rationale: Consistency with overloaded assignment or assignment-like operations, a = b can be read as performSomeCopy(a, b).

However, the concept of “order of evaluation” is only applicable after the code was normalized: The normalization involves template expansions and argument reorderings that have been passed to named parameters:

  1. var s = ""
  2. proc p(): int =
  3. s.add "p"
  4. result = 5
  5. proc q(): int =
  6. s.add "q"
  7. result = 3
  8. # Evaluation order is 'b' before 'a' due to template
  9. # expansion's semantics.
  10. template swapArgs(a, b): untyped =
  11. b + a
  12. doAssert swapArgs(p() + q(), q() - p()) == 6
  13. doAssert s == "qppq"
  14. # Evaluation order is not influenced by named parameters:
  15. proc construct(first, second: int) =
  16. discard
  17. # 'p' is evaluated before 'q'!
  18. construct(second = q(), first = p())
  19. doAssert s == "qppqpq"

Rationale: This is far easier to implement than hypothetical alternatives.