Why Nim

Nim is a statically typed, compiled language that reads like Python and runs like C. This lesson explains what Nim is, where it came from, which problems it solves, and the mental model you need before writing your first line of code.

What Nim Is

Nim is a general-purpose, statically typed, compiled language. Source code is compiled to C, C++, Objective-C or JavaScript, and the native backends are then handed to a system compiler such as GCC, Clang or MSVC. That single design decision explains most of what follows: Nim programs are ordinary native binaries with an ordinary C ABI, yet the language you write is high level, expressive and readable.

Three Promises, Checked

The language makes three claims. A good engineer should test every claim, so here is each one with the mechanism that actually delivers it.

PromiseMechanism that delivers it
Runs like CAhead-of-time compilation to native code through a C/C++ backend; value types by default; zero-cost C interop.
Reads like PythonIndentation-based blocks, no braces or semicolons, rich operators, uniform call syntax.
No boilerplate for metaprogrammingCompile-time evaluation, hygienic templates and AST macros in the same language as the application.

Hello, World

The smallest complete program. echo is a procedure that comes from the system module, which every Nim module imports implicitly — that is why there is no import and no main function.

# Module-level statements execute in order, top to bottom, when the program starts.
echo "Hello, Nim!"   # echo writes to stdout and appends a newline

# Comments run from '#' to the end of the line. There is no block comment token,
# but a multi-line #[ ... ]# form exists for temporarily disabling code.

Compile and run it with one command: nim r hello.nim. The r command means "compile, then run the produced binary".

Design Goals

Nim was designed around a small number of priorities. Knowing them turns surprising rules into obvious ones.

Performance Without Cargo Cult

There is no virtual machine and no interpreter at run time: the compiler generates C, the C compiler generates machine code. Objects are value types unless you ask for ref, so your data layout is predictable, and memory management is a compile-time choice (--mm:orc is the default) rather than a fixed run-time service.

Readability as a Feature

Blocks are defined by indentation, so there is no brace style to argue about. Declarations are keyword-first (proc, let, type), and method-call syntax is uniform, which lets a library read like prose: line.split(",").map(parseInt).join("|").

Compile-Time Computation

Templates and macros are written in Nim itself, not in a separate macro language, and they run over an abstract syntax tree. That means a compile-time error from a macro points at the caller's line, and a library can validate a schema while the program is still being built.

History and Versions

Nim is not a hobby experiment that grew sideways; it has a twenty-year track record, which matters when you decide to depend on it.

Origins

Andreas Rumpf started the project in 2005 as a personal exploration of a compiled language with a Python-like feel. It was released publicly in 2008 under the name Nimrod, renamed to Nim in 2014, and reached its first stable, backward-compatible release in September 2019.

Versions You Will Meet

Two releases matter for modern code. Version 1.0 fixed the language and the standard library API; version 2.0 (August 2023) made ORC memory management the default and turned threads on for ordinary builds.

VersionReleaseWhat changed for you
1.0September 2019First stable API; the beginning of backward compatibility guarantees.
2.0August 2023ORC memory management and threading are the defaults; refined destructors; move semantics complete.
2.x (current)2024 and laterNew split modules (std/paths, std/dirs, std/files, std/envvars), a stable std/taskpools threading story, and incremental cleanup of the monolithic std/os.

The Mental Model

Three ideas explain almost every Nim design decision you will meet in this track: the compilation pipeline, uniform call syntax, and style-insensitive identifiers. Learn them now and the rest of the language becomes predictable.

From Source to Binary

The Nim compiler does not write machine code itself for the native targets. It type-checks your program, expands templates and macros, and then emits C, C++ or Objective-C source, which a normal system compiler turns into an executable. This is why Nim binaries link against the same libraries as C programs, and why a C profiler or debugger works on them without special support.

Nim source compiled to C, then to a native binary

Figure 1 — the Nim compiler emits C; the C compiler produces the native binary you ship.

Uniform Call Syntax

Any procedure can be called as if its first argument were an object it belongs to. There is no separate class of "methods" that only exist inside types: x.f(y) and f(x, y) are the same call. This is what makes library code read like a pipeline.

proc double(x: int): int = x * 2   # an ordinary procedure with one parameter

echo double(21)    # classic call form:  42
echo 21.double()   # method-call form:  42 — identical procedure, same result

import std/strutils
# Both lines below call the same split procedure; the receiver becomes argument 1.
let a = split("a,b,c", ',')   # the plain form
let b = "a,b,c".split(',')    # the UFCS form, far easier to read in a chain
doAssert a == b
echo b.len, " fields"         # len is also just a procedure: len(b)

Case and Style Insensitivity

Nim ignores case (except for the very first character) and underscores inside identifiers, so parseInt, parse_int and parseInt refer to the same symbol. The first letter is significant, which is how types such as MyType stay distinct from values such as myType.

from std/strutils import parseInt

# These three spellings resolve to exactly one procedure in the standard library.
echo parseInt("42")      # 42
echo "42".parse_int()    # 42
# echo ParseInt("42")    # compile error: the first letter counts, so this is a new name

# Nothing forces you to use the canonical style, but nimpretty rewrites code to
# it, and every library in the ecosystem follows the same rule: camelCase for
# procedures and variables, PascalCase for types. Style-insensitivity is what
# lets both spellings coexist while the formatter normalizes them for you.

Next Steps

This lesson gave you the map; the following lessons give you the terrain. Nothing here requires memorizing flags or trivia — the goal is that when you write Nim you always know which layer is doing the work.

How to Study This Track

Follow the phases in order. Each phase builds on the previous one: types and control flow before collections and text, abstractions before generics and macros, and system-level topics after you are fluent in everyday code. Every lesson ends with commented examples you can compile.

Practice Before Theory Repeats

Install the toolchain in the next lesson, then keep the Lab Examples page open while you read: each demo is a single self-contained file with the comments that explain the intent. Reading working code and modifying it is the fastest path from understanding to skill.