Procedures

What may be called functions elsewhere are known as procedures in Nim.

Unlike other languages, Nim doesn't strictly require that you define any procedures at all - you can simply write all of your code at the top level. However, procedures are extremely useful for organising your logic into reusable components.

Every procedure has a name, an optional list of arguments that are passed to it and a return type. The most simple procedure definition takes no arguments and returns nothing:

proc simpleProcedure =
  echo "This is a simple procedure"

proc defines a new procedure, which is named simpleProcedure. The procedure name is followed by an = symbol separating it from the procedure body. The procedure body is indented one level from the definition.

A procedure that takes no arguments and does nothing is fairly useless, so how about a function that takes an argument?

proc sayHello(name: string) =
  echo "Hello, " & name

This procedure, sayHello, take s asingle name argument which is a string. This procedure can be called as follows:

proc sayHello(name: string) =
  echo "Hello, " & name

sayHello("Euan")

In order to include multiple arguments to a function, you can separate them by a comma (,). If all of the arguments are the same type, you need only define the type once:

proc addTwoInts(x, y: int): int =
  result = x + y

Now we're getting slightly more complex! This procedure takes two integer arguments, and adds them together returning an integer value. Note that we define the type that the procedure returns using the same syntax that we use to define the type of a variable.

Also note that we are using the implicit result value to return the result of the procedure. All procedures in Nim automatically have a result variable which will be returned from the procedure.

You can also provide a default vlaue for arguments in procedures, meaning they do not have to be supplied when the procedure is called.

proc addTwoInts(x = 2, y = 10): int
  result = x + y

Note that this procedure is also inferring the type of the arguments based upon the default argument values! You may also define the type manually if you wish.

Procedure overloading

It's possible to define multiple procedures with the same name that have different arguments. The correct procedure will be chosen based upon the parameters passed to it:

proc toLower(c: char): char = # toLower for characters
  if c in {'A'..'Z'}:
    result = chr(ord(c) + (ord('a') - ord('A')))
  else:
    result = c

proc toLower(s: string): string = # toLower for strings
  result = newString(len(s))
  for i in 0..len(s) - 1:
    result[i] = toLower(s[i]) # calls toLower for characters; no recursion!

Using named arguments

It's also possible to supply arguments in a different order using named arguments when calling a procedure. This is a useful feature when used with procedures with default argument values where you may wish to override one argument at the end of the argument list:

proc foo(day: string = "Monday", month: string = "December", year: int = 2016) =
  echo "The day is " & day & ", the month is " & month & "the year is " & $year

# Call the `foo` function, but the year is 2017 not 2016!
foo(year=2017)

Early return from procedures

It's possible to explicitly return a value from a procedure, which can be useful if you need to return from a function early due to some condition:

proc addTwoInts(x, y: int): int =
  return 42

  # The following code is never ran!
  result = x + y

Procedure pointers

It's also possible to assign a procedure to a variable, allowing you to pass procedures to other procedures:

var f: proc(x, y: int): int

f = proc(x, y: int): int =
    result = x + y

echo f(10, 2)