The Effect System

20.1 Exception Tracking (raises)

The compiler infers effects. The raises annotation makes them part of the API contract.

A common pattern: {.push raises: [].} at the top of a module (used heavily in Status' codebases).

proc safeDiv(a, b: int): int {.raises: [DivByZeroDefect].} = a div b

proc parse(s: string): int {.raises: [ValueError].} = parseInt(s)

proc noThrow() {.raises: [].} =      # compiler verifies that no exceptions escape
  try: discard parse("x")
  except ValueError: discard

20.2 Tag Tracking (tags)

Built-in tags: ReadIOEffect, WriteIOEffect, TimeEffect, ExecIOEffect, RootEffect.

forbids pragma (Nim 2): proc f() {.forbids: [IOEffect].}.

type
  DbEffect = object of RootEffect
proc queryDb() {.tags: [DbEffect].} = discard
proc pureLogic() {.tags: [].} = discard      # must not call queryDb

20.3 Side Effects and func

func = proc {.noSideEffect.}: can't access global mutable state, I/O, or call side-effecting procs.

--experimental:strictFuncs additionally forbids mutating data reachable through ref params.

Debug escape hatch: {.cast(noSideEffect).}: echo "debug".

20.4 GC Safety

{.gcsafe.}: the proc doesn't access GC'd global memory, which is required for thread procs.

{.cast(gcsafe).}: ... to override (unsafe).