Procedures

What most programming languages call methods or functions are called procedures in Nim. A procedure declaration consists of an identifier, zero or more formal parameters, a return value type and a block of code. Formal parameters are declared as a list of identifiers separated by either comma or semicolon. A parameter is given a type by : typename. The type applies to all parameters immediately before it, until either the beginning of the parameter list, a semicolon separator or an already typed parameter, is reached. The semicolon can be used to make separation of types and subsequent identifiers more distinct.

  1. # Using only commas
  2. proc foo(a, b: int, c, d: bool): int
  3. # Using semicolon for visual distinction
  4. proc foo(a, b: int; c, d: bool): int
  5. # Will fail: a is untyped since ';' stops type propagation.
  6. proc foo(a; b: int; c, d: bool): int

A parameter may be declared with a default value which is used if the caller does not provide a value for the argument.

  1. # b is optional with 47 as its default value
  2. proc foo(a: int, b: int = 47): int

Parameters can be declared mutable and so allow the proc to modify those arguments, by using the type modifier var.

  1. # "returning" a value to the caller through the 2nd argument
  2. # Notice that the function uses no actual return value at all (ie void)
  3. proc foo(inp: int, outp: var int) =
  4. outp = inp + 47

If the proc declaration has no body, it is a forward declaration. If the proc returns a value, the procedure body can access an implicitly declared variable named result that represents the return value. Procs can be overloaded. The overloading resolution algorithm determines which proc is the best match for the arguments. Example:

  1. proc toLower(c: char): char = # toLower for characters
  2. if c in {'A'..'Z'}:
  3. result = chr(ord(c) + (ord('a') - ord('A')))
  4. else:
  5. result = c
  6. proc toLower(s: string): string = # toLower for strings
  7. result = newString(len(s))
  8. for i in 0..len(s) - 1:
  9. result[i] = toLower(s[i]) # calls toLower for characters; no recursion!

Calling a procedure can be done in many different ways:

  1. proc callme(x, y: int, s: string = "", c: char, b: bool = false) = ...
  2. # call with positional arguments # parameter bindings:
  3. callme(0, 1, "abc", '\t', true) # (x=0, y=1, s="abc", c='\t', b=true)
  4. # call with named and positional arguments:
  5. callme(y=1, x=0, "abd", '\t') # (x=0, y=1, s="abd", c='\t', b=false)
  6. # call with named arguments (order is not relevant):
  7. callme(c='\t', y=1, x=0) # (x=0, y=1, s="", c='\t', b=false)
  8. # call as a command statement: no () needed:
  9. callme 0, 1, "abc", '\t' # (x=0, y=1, s="abc", c='\t', b=false)

A procedure may call itself recursively.

Operators are procedures with a special operator symbol as identifier:

  1. proc `$` (x: int): string =
  2. # converts an integer to a string; this is a prefix operator.
  3. result = intToStr(x)

Operators with one parameter are prefix operators, operators with two parameters are infix operators. (However, the parser distinguishes these from the operator’s position within an expression.) There is no way to declare postfix operators: all postfix operators are built-in and handled by the grammar explicitly.

Any operator can be called like an ordinary proc with the ‘opr’ notation. (Thus an operator can have more than two parameters):

  1. proc `*+` (a, b, c: int): int =
  2. # Multiply and add
  3. result = a * b + c
  4. assert `*+`(3, 4, 6) == `+`(`*`(a, b), c)