Anatomy of a Nim file
Nim is a statically typed, compiled systems programming language known for its efficiency, expressiveness, and elegance.
File extensions: .nim
Every part of the example below is labelled and explained. This page is one of 55 annotated tours on AnatomyOf, a free, open-source project by LunarWerx Studios.
What is inside a Nim file
Comment (single-line)
Everything after # on a line is ignored by the compiler.
A single-line comment starts with # and runs to the end of the line. The compiler discards it entirely before semantic analysis, so it costs nothing at compile time or runtime -- unlike a sleep call, which Nim programmers insist is "just a very thorough comment." Comments can appear on their own line or trail actual code, as in x = 1 # sets x. Nim also supports documentation comments (##) that nim doc extracts into generated HTML documentation, so writing them well pays off twice.
Comment (block)
A multi-line comment wrapped in #[ ... ]#, also ignored by the compiler.
Block comments open with #[ and close with ]#, and unlike C-style /* */ comments, Nim block comments nest -- you can comment out a region that already contains a block comment without the compiler getting confused about where it ends. They are typically used for longer explanations at the top of a file or to temporarily disable a chunk of code during debugging. Because nesting works, "comment out the whole module to bisect the bug" is a safe, if slightly embarrassing, debugging technique.
Import statement
Brings in modules from the standard library or third-party packages.
import std/strutils pulls in a standard library module, exposing its public procs, types, and templates in the current scope. The std/ prefix explicitly names the standard library, which disambiguates it from a same-named package a project might install via Nimble. Multiple modules can be imported on one line (import std/[strutils, sequtils]), and import foo except bar or from foo import only: bar let you control exactly what enters scope, which keeps large programs from drowning in identifier collisions.
Type definition (object)
Defines a custom data structure with type and object.
A type block introduces one or more type definitions; object declares a record type with named, typed fields, similar to a struct in C or a dataclass in Python. Fields are plain values by default (no reference semantics), so assigning one Person to another copies it. Objects support inheritance via object of RootObj, optional fields through case objects (Nim's tagged unions/variant objects), and can be marked ref object when heap allocation and reference semantics are wanted instead.
Procedure definition (proc)
Defines a function or procedure using proc.
proc declares a procedure: a named, typed, reusable block of code. Parameters are written name: Type, and the return type follows a : after the parameter list -- if omitted, the proc returns nothing (Nim's void). Procs are Nim's workhorse abstraction: they can be generic, take default argument values, and be marked with effects like {.noSideEffect.} for extra compiler-checked guarantees. A one-expression body can skip the newline-and-indent entirely, as in proc greet(p: Person) = echo ....
Variable declaration & object instantiation
Declares a variable and constructs an object instance.
var declares a mutable variable; Nim infers its type from the initializer, so var alice = Person(name: "Alice", age: 30) needs no explicit type annotation. The parenthesized, named-field syntax after the type is object construction -- each field is set explicitly by name, so field order in the source never matters. Nim also offers let for a single-assignment binding (preferred whenever a value never changes) and const for a value computed entirely at compile time. Reaching for var by default and tightening to let later is a common, low-drama refactor.
Control flow (loop)
Repeats code with for or while.
for i in 1..3: iterates over a range -- 1..3 is inclusive on both ends, giving 1, 2, 3, which trips up newcomers expecting Python's exclusive-upper-bound range. Nim also provides 1..<3 for an explicitly exclusive upper bound and countdown(3, 1) for iterating backwards. while repeats as long as its condition holds, and both loop kinds support break and continue. Iterators in Nim are themselves a first-class language feature (iterator), so for x in myIterator(): works over user-defined sequences just as naturally as over a built-in range.
Control flow (conditional)
Executes code based on a condition (if, elif, else).
if/elif/else branch on a bool expression -- Nim has no implicit truthiness, so an int or a string can never silently stand in for a condition the way it can in C or Python. Blocks are delimited by a colon and indentation, not braces. if is also an expression in Nim: let msg = if age > 32: "older" else: "younger" yields a value directly, which often replaces what other languages need a ternary operator for.
Main execution block
Code that runs only when this file is the entry module.
when isMainModule: is a compile-time conditional (when, not if) that is only true in the module Nim was asked to compile directly -- when the file is imported by another module instead, that block is skipped entirely, at compile time, with zero runtime cost. This mirrors Python's if __name__ == "__main__": guard but is resolved during compilation rather than at runtime, since when conditions must be evaluable by the compiler itself. It is the idiomatic place to put a script's "run this as a program" logic while keeping the module safely importable elsewhere.
Procedure call
Executes a previously defined procedure.
greet(alice) calls the greet proc with alice as its argument, using ordinary function-call syntax. Nim also supports method-call syntax -- alice.greet() -- for the exact same call, and the two forms are freely interchangeable, which is why Nim code reads comfortably in both a functional and an object-oriented style. For a proc taking no arguments, the parentheses are optional at the call site (doStuff and doStuff() are equivalent), which occasionally makes a bare identifier and a zero-argument call visually indistinguishable -- context, and the compiler, sort it out.
Official Nim site · All languages on AnatomyOf
The interactive tour needs JavaScript. Enable it to hover a callout and trace it into the code.