Functions in Nim
Learn how to define and use functions in Nim - procedures, parameters, default values, the result variable, recursion, and higher-order procs with Docker-ready examples
Functions are the primary unit of code reuse in Nim. They package logic behind a name, accept inputs as parameters, and hand back results - letting you build programs from small, testable pieces rather than one long script.
Nim is genuinely multi-paradigm, and its function model reflects that. The same proc keyword that defines a plain procedure can also be passed around as a first-class value, returned from another proc, or captured in a closure. Nim adds a dedicated func keyword to mark side-effect-free functions, and an implicit result variable that makes returning values feel effortless. Thanks to static typing, every parameter and return type is checked at compile time, so a mismatched argument is a build error, not a runtime surprise.
In this tutorial you’ll define procedures with typed parameters and return values, use default arguments and named calls, modify caller variables with var parameters, write recursive procedures, and pass procedures to other procedures as higher-order functions. Each example is self-contained and runnable through Docker.
Defining Procedures
The core building block is proc. A procedure lists its parameters with their types, optionally declares a return type after :, and produces a value either through the implicit result variable or by leaving an expression as the last line of its body.
Create a file named functions_basics.nim:
| |
Notice the last line: Nim’s Uniform Function Call Syntax (UFCS) lets you write 10.add(5) instead of add(10, 5). The first argument simply moves in front of the dot, which is why method-style calls like greeter.greet() work without defining methods on a class.
Default Parameters and Named Arguments
Parameters can declare default values, making them optional at the call site. Callers may also pass arguments by name in any order, which keeps calls readable when a procedure has several parameters.
Create a file named functions_defaults.nim:
| |
The result variable is automatically declared and initialized (to 1 here only because we assign it; numeric defaults are otherwise 0). You mutate it through the body and it is returned when the procedure ends - no explicit return statement needed.
Var Parameters and Pure Functions
By default, parameters are immutable inside a procedure. To let a procedure modify the caller’s variable, mark the parameter var (pass-by-reference). When a procedure has no side effects at all, declare it with func - the compiler then rejects any hidden side effect, documenting and enforcing purity.
Create a file named functions_var.nim:
| |
A func cannot call echo, mutate global state, or perform I/O - those are side effects. This makes func ideal for pure calculations and lets the compiler reason more aggressively about your code.
Recursion
A procedure may call itself. Recursion expresses naturally repetitive, self-similar problems - like computing a factorial or a Fibonacci number - without an explicit loop. Each call works on a smaller input until it reaches a base case.
Create a file named recursion.nim:
| |
Here the if/else is an expression: whichever branch runs supplies the procedure’s return value. This is why neither branch needs result = or return.
Higher-Order Functions and Closures
Because procedures are first-class values in Nim, you can pass one procedure to another, store procedures in variables, and return new procedures. A procedure that captures variables from its surrounding scope is a closure - the foundation of Nim’s functional style.
Create a file named higher_order.nim:
| |
makeAdder(10) returns a brand-new procedure that remembers amount = 10. Calling add10(7) then yields 17. This pattern - functions that build other functions - is what makes higher-order programming so expressive.
Running with Docker
You don’t need Nim installed locally. Pull the official image once, then compile and run each example with nim c -r (compile to C, then run).
| |
Expected Output
functions_basics.nim:
add(3, 4) = 7
multiply(5, 6) = 30
Hello, Nim!
10.add(5) = 15
functions_defaults.nim:
power(5) = 25
power(2, 8) = 256
power(base = 3, exponent = 3) = 27
functions_var.nim:
After double: 42
square(9) = 81
recursion.nim:
factorial(5) = 120
fib(10) = 55
higher_order.nim:
applyTwice(increment, 10) = 12
applyTwice(square, 5) = 625
add10(7) = 17
Key Concepts
procis the workhorse - it defines procedures that take typed parameters and optionally return a typed value;funcis the same thing with a compiler-enforced no-side-effects guarantee.- The implicit
resultvariable is pre-declared in every procedure with a return type; assign to it and it is returned automatically, with noreturnstatement required. - Last expression returns - if the final line of a proc body is an expression matching the return type, its value is returned, so
if/elseand single-line bodies work withoutresult. - Default and named arguments make procedures flexible: parameters can supply default values, and callers can pass arguments by name in any order.
varparameters enable pass-by-reference so a procedure can modify the caller’s variable in place; ordinary parameters are immutable inside the body.- Uniform Function Call Syntax (UFCS) means
obj.proc(args)andproc(obj, args)are interchangeable, giving Nim its clean method-style calls without true methods. - Procedures are first-class values - they can be passed as arguments, returned from other procs, and captured in closures, enabling higher-order functional programming.
Running Today
All examples can be run using Docker:
docker pull nimlang/nim:alpine
Comments
Loading comments...
Leave a Comment