Hubbry Logo
Nim (programming language)Nim (programming language)Main
Open search
Nim (programming language)
Community hub
Nim (programming language)
logo
8 pages, 0 posts
0 subscribers
Be the first to start a discussion here.
Be the first to start a discussion here.
Nim (programming language)
Nim (programming language)
from Wikipedia

Nim
The Nim crown logo
ParadigmsMulti-paradigm: compiled, concurrent, procedural, imperative, functional, object-oriented, meta
Designed byAndreas Rumpf
DeveloperNim Lang Team[1]
First appeared2008; 18 years ago (2008)
Stable release
2.2.6[2] Edit this on Wikidata / 31 October 2025; 3 months ago (31 October 2025)
Typing disciplineStatic,[3] strong,[4] inferred, structural
ScopeLexical
Implementation languagePascal (2005–2008)
Nim (2008–present, self-hosted)
PlatformIA-32, x86-64, ARM, Aarch64, RISC-V, PowerPC ...[5]
OSCross-platform[6]
LicenseMIT License[7] Edit this on Wikidata
Filename extensions.nim, .nims, .nimble
Websitenim-lang.org
Influenced by
Ada, Modula-3, Lisp, C++, Object Pascal, Python, Oberon, Rust, ParaSail[8]

Nim is a general-purpose, multi-paradigm, statically typed, compiled, high-level system programming language.[9] It was designed and developed by a team led by Andreas Rumpf. Nim aims to be "efficient, expressive, and elegant",[10] and supports metaprogramming, functional, message passing,[11] procedural, and object-oriented programming paradigms. Nim includes features such as compile-time code generation, algebraic data types, and a foreign function interface (FFI) for interfacing with C, C++, Objective-C, and JavaScript. It also supports compilation to these same languages as intermediate representations.

Description

[edit]

Nim is statically typed.[12] It supports compile-time metaprogramming features such as syntactic macros and term rewriting macros.[13] Term rewriting macros enable library implementations of common data structures, such as bignums and matrices, to be implemented efficiently and with syntactic integration, as if they were built-in language facilities.[14] Iterators are supported and can be used as first class entities,[13] as can functions, allowing for the use of functional programming methods. Object-oriented programming is supported by inheritance and multiple dispatch. Functions can be generic and overloaded, and generics are further enhanced by Nim's support for type classes. Operator overloading is also supported.[13] Nim includes multiple tunable memory management strategies, including tracing garbage collection, reference counting, and fully manual systems, with the default being deterministic reference counting with optimizations via move semantics and cycle collection via trial deletion.[15]

[Nim] ... presents a most original design that straddles Pascal and Python and compiles to C code or JavaScript.[16]

— Andrew Binstock, editor-in-chief of Dr. Dobb's Journal, 2014

As of August 2023, Nim compiles to C, C++, JavaScript, Objective-C,[17] and LLVM.[18]

History

[edit]
Branch Version Release date[19]
0.x Unsupported: 0.10.2 2014-12-29
Unsupported: 0.11.2 2015-05-04
Unsupported: 0.12.0 2015-10-27
Unsupported: 0.13.0 2016-01-18
Unsupported: 0.14.2 2016-06-09
Unsupported: 0.15.2 2016-10-23
Unsupported: 0.16.0 2017-01-08
Unsupported: 0.17.2 2017-09-07
Unsupported: 0.18.0 2018-03-01
Unsupported: 0.19.6 2019-05-13
Unsupported: 0.20.2 2019-06-17
1.0 Unsupported: 1.0.0 2019-09-23
Unsupported: 1.0.10 2020-10-27
1.2 Unsupported: 1.2.0 2020-04-03
Unsupported: 1.2.18 2022-02-09
1.4 Unsupported: 1.4.0 2020-10-16
Unsupported: 1.4.8 2021-05-25
1.6 Unsupported: 1.6.0 2021-10-19
Unsupported: 1.6.20 2024-04-16
2.0 Unsupported: 2.0.0 2023-08-01
Supported: 2.0.16 2025-04-22
2.2 Unsupported: 2.2.0 2024-10-02
Latest version: 2.2.6 2025-10-31
Legend:
Unsupported
Supported
Latest version
For each 0.x branch, only the latest point release is listed.
For later branches, the first and the latest point release is listed.

Andreas Rumpf is the designer and original implementer of Nim. He received a diploma in computer science from the University of Kaiserslautern-Landau, Germany. His research interests include hard real-time systems, embedded systems, compiler construction and artificial intelligence.[20]

Nim's initial development was started in 2005 under the name Nimrod and was made public in 2008.[21]: 4–11 

The first version of the Nim compiler was written in Pascal using the Free Pascal compiler.[22] In 2008, a version of the compiler written in Nim was released.[23] The compiler is free and open-source software, and is being developed by a community of volunteers working with Andreas Rumpf.[24] The language was officially renamed from Nimrod to Nim with the release of version 0.10.2 in December 2014.[25] On 23 September 2019, version 1.0 of Nim was released, signifying the maturing of the language and its toolchain. On 1 August 2023, version 2.0 of Nim was released, signifying the completion, stabilization of, and switch to the ARC/ORC memory model.[26]

Language design

[edit]

Syntax

[edit]

The syntax of Nim resembles that of Python.[27] Code blocks and nesting statements are identified through use of whitespace, according to the offside-rule. Many keywords are identical to their Python equivalents, which are mostly English keywords, whereas other programming languages usually use punctuation. With the goal of improving upon its influence languages, even though Nim supports indentation-based syntax like Python, it introduced additional flexibility. For example, a single statement may span multiple lines if a comma or binary operator is at the end of each line. Nim also supports user-defined operators.

Unlike Python, Nim implements (native) static typing. Nim's type system allows for easy type conversion, casting, and provides syntax for generic programming. Nim notably provides type classes which can stand in for multiple types, and provides several such type classes 'out of the box'. Type classes allow working with several types as if they were a single type. For example:

  • openarray – Represents arrays of different sizes, sequences, and strings
  • SomeSignedInt – Represents all the signed integer types
  • SomeInteger – Represents all the Integer types, signed or not
  • SomeOrdinal – Represents all the basic countable and ordered types, except of non integer number

This code sample demonstrates the use of typeclasses in Nim:

# Let's declare a function that takes any type of number and displays its double
# In Nim functions with side effect are called "proc"
proc timesTwo(i: SomeNumber) =
  echo i * 2
# Let's write another function that takes any ordinal type, and returns
# the double of the input in its original type, if it is a number;
# or returns the input itself otherwise.
# We use a generic Type(T), and precise that it can only be an Ordinal
func twiceIfIsNumber[T: SomeOrdinal](i: T): T =
  when T is SomeNumber: # A `when` is an `if` evaluated during compile time
    result = i * 2 # You can also write `return i * 2`
  else:
    # If the Ordinal is not a number it is converted to int,
    # multiplied by two, and reconverted to its based type
    result = (i.int * 2).T
echo twiceIfIsNumber(67) # Passes an int to the function
echo twiceIfIsNumber(67u8) # Passes an uint8
echo twiceIfIsNumber(true) # Passes a bool (Which is also an Ordinal)

Influence

[edit]

According to the language creator, Nim was conceived to combine the best parts of Ada typing system, Python flexibility, and powerful Lisp macro system.[28]

Nim was influenced by specific characteristics of existing languages, including the following:

Uniform function call syntax

[edit]

Nim supports uniform function call syntax (UFCS)[29] and identifier equality, which provides a large degree of flexibility in use.

For example, each of these lines print "hello world", just with different syntax:

echo "hello world"
echo("hello world")
"hello world".echo()
"hello world".echo
echo("hello", " world")
"hello".echo(" world")
"hello".echo " world"

Identifier equality

[edit]

Nim is almost fully style-insensitive; two identifiers are considered equal if they only differ by capitalization and underscores, as long as the first characters are identical. This is to enable a mixture of styles across libraries: one user can write a library using snake_case as a convention, and it can be used by a different user in a camelCase style without issue.[30]

const useHttps = true
assert useHttps == useHttps
assert useHTTPS == useHttps
assert use_https == useHttps

Stropping

[edit]

The stropping feature allows the use of any name for variables or functions, even when the names are reserved words for keywords. An example of stropping is the ability to define a variable named if, without clashing with the keyword if. Nim's implementation of this is achieved via backticks, allowing any reserved word to be used as an identifier.[31]

type Type = object
  `int`: int

let `object` = Type(`int`: 9)
assert `object` is Type
assert `object`.`int` == 9

var `var` = 42
let `let` = 8
assert `var` + `let` == 50

const `assert` = true
assert `assert`

Compiler

[edit]

The Nim compiler emits fast, optimized C code by default. It defers compiling-to-object code to an external C compiler[32] to leverage existing compiler optimization and portability. Many C compilers are supported, including Clang, Microsoft Visual C++ (MSVC), MinGW, and GNU Compiler Collection (GCC). The Nim compiler can also emit C++, Objective-C, and JavaScript code to allow easy interfacing with application programming interfaces (APIs) written in those languages;[9] developers can simply write in Nim, then compile to any supported language. This also allows writing applications for iOS and Android. There is also an unofficial LLVM backend, allowing use of the Nim compiler in a stand-alone way.[18]

The Nim compiler is self-hosting, meaning it is written in the Nim language.[33] The compiler supports cross-compiling, so it is able to compile software for any of the supported operating systems, no matter the development machine. This is useful for compiling applications for embedded systems, and for uncommon and obscure computer architectures.[citation needed]

Compiler options

[edit]

By default, the Nim compiler creates a debug build.[34] With the option -d:release a release build can be created, which is optimized for speed and contains fewer runtime checks.[34] With the option -d:danger all runtime checks can be disabled, if maximum speed is desired.[34]

Memory management

[edit]

Nim supports multiple memory management strategies, including the following:[35]

  • --mm:arc – Automatic reference counting (ARC) with move semantics optimizations, offers a shared heap. It offers fully deterministic performance for hard realtime systems.[36] Reference cycles may cause memory leaks: these may be dealt with by manually annotating {.acyclic.} pragmas or by using --mm:orc.
  • --mm:orc – Same as --mm:arc but adds a cycle collector (the "O") based on "trial deletion".[37] The cycle collector only analyzes types if they are potentially cyclic.
  • --mm:refc – Standard deferred reference counting based garbage collector with a simple mark-and-sweep backup GC in order to collect cycles. Heaps are thread-local.
  • --mm:markAndSweep – Simple mark-and-sweep based garbage collector. Heaps are thread-local.
  • --mm:boehmBoehm based garbage collector, it offers a shared heap.
  • --mm:goGo's garbage collector, useful for interoperability with Go. Offers a shared heap.
  • --mm:none – No memory management strategy nor a garbage collector. Allocated memory is simply never freed, unless manually freed by the developer's code.

As of Nim 2.0, ORC is the default GC.[26]

Development tools

[edit]

Bundled

[edit]

Many tools are bundled with the Nim install package, including:

Nimble

[edit]

Nimble is the standard package manager used by Nim to package Nim modules.[38] It was initially developed by Dominik Picheta, who is also a core Nim developer. Nimble has been included as Nim's official package manager since 27 October 2015, the v0.12.0 release.[39]

Nimble packages are defined by .nimble files, which contain information about the package version, author, license, description, dependencies, and more.[21]: 132  These files support a limited subset of the Nim syntax called NimScript, with the main limitation being the access to the FFI. These scripts allow changing of test procedure, or for custom tasks to be written.

The list of packages is stored in a JavaScript Object Notation (JSON) file which is freely accessible in the nim-lang/packages repository on GitHub. This JSON file provides Nimble with a mapping between the names of packages and their Git or Mercurial repository URLs.

Nimble comes with the Nim compiler. Thus, it is possible to test the Nimble environment by running: nimble -v. This command will reveal the version number, compiling date and time, and Git hash of nimble. Nimble uses the Git package, which must be available for Nimble to function properly. The Nimble command-line is used as an interface for installing, removing (uninstalling), and upgrading–patching module packages.[21]: 130–131 

c2nim

[edit]

c2nim is a source-to-source compiler (transcompiler or transpiler) meant to be used on C/C++ headers to help generate new Nim bindings.[40] The output is human-readable Nim code that is meant to be edited by hand after the translation process.

koch

[edit]

koch is a maintenance script that is used to build Nim, and provide HTML documentation.[41]

nimgrep

[edit]

nimgrep is a generic tool for manipulating text. It is used to search for regex, peg patterns, and contents of directories, and it can be used to replace tasks. It is included to assist with searching Nim's style-insensitive identifiers.[42]

nimsuggest

[edit]

nimsuggest is a tool that helps any source code editor query a .nim source file to obtain useful information like definition of symbols or suggestions for completions.[43]

niminst

[edit]

niminst is a tool to generate an installer for a Nim program.[44] It creates .msi installers for Windows via Inno Setup, and install and uninstall scripts for Linux, macOS, and Berkeley Software Distribution (BSD).

nimpretty

[edit]

nimpretty is a source code beautifier, used to format code according to the official Nim style guide.[45]

Testament

[edit]

Testament is an advanced automatic unit tests runner for Nim tests. Used in developing Nim, it offers process isolation tests, generates statistics about test cases, supports multiple targets and simulated Dry-Runs, has logging, can generate HTML reports, can skip tests from a file, and more.

Other notable tools

[edit]

Some notable tools not included in the Nim distribution include:

choosenim

[edit]

choosenim was developed by Dominik Picheta, creator of the Nimble package manager, as a tool to enable installing and using multiple versions of the Nim compiler. It downloads any Nim stable or development compiler version from the command line, enabling easy switching between them.[46]

nimpy

[edit]

nimpy is a library that enables convenient Python integration in Nim programs.[47]

pixie

[edit]

pixie is a feature-rich 2D graphics library, similar to Cairo or the Skia. It uses SIMD acceleration to speed-up image manipulation drastically. It supports many image formats, blending, masking, blurring, and can be combined with the boxy library to do hardware accelerated rendering.

nimterop

[edit]

nimterop is a tool focused on automating the creation of C/C++ wrappers needed for Nim's foreign function interface.[48]

Libraries

[edit]

Pure/impure libraries

[edit]

Pure libraries are modules written in Nim only. They include no wrappers to access libraries written in other programming languages.

Impure libraries are modules of Nim code which depend on external libraries that are written in other programming languages such as C.

Standard library

[edit]

The Nim standard library includes modules for all basic tasks, including:[49]

  • System and core modules
  • Collections and algorithms
  • String handling
  • Time handling
  • Generic Operating System Services
  • Math libraries
  • Internet Protocols and Support
  • Threading
  • Parsers
  • Docutils
  • XML Processing
  • XML and HTML code generator
  • Hashing
  • Database support (PostgreSQL, MySQL and SQLite)
  • Wrappers (Win32 API, POSIX)

Use of other libraries

[edit]

A Nim program can use any library which can be used in a C, C++, or JavaScript program. Language bindings exist for many libraries, including GTK,[50][51] Qt QML,[52] wxWidgets,[53] SDL 2,[54][55] Raylib,[56] Godot,[57] UE5,[58] Cairo,[59] OpenGL,[60] Vulkan,[61] WinAPI,[62] zlib, libzip, OpenSSL and cURL.[63] Nim works with PostgreSQL, MySQL, and SQLite databases.

There are open source tools of various degree of support that can be used to interface Nim with Lua,[64] Julia,[65] Rust,[66] C#,[67] and Python[68] programming languages or transpile Nim to TypeScript.[69]

Examples

[edit]

Hello world

[edit]

The "Hello, World!" program in Nim:

echo("Hello, World!")
# Procedures can be called with no parentheses
echo "Hello, World!"

Another version of "Hello World" can be accomplished by calling the write function with the stdout stream:

stdout.write("Hello, World!\n")
write(stdout, "Hello, World!\n")

Fibonacci

[edit]

Several implementations of the Fibonacci function, showcasing implicit returns, default parameters, iterators, recursion, and while loops:

proc fib(n: Natural): Natural =
  if n < 2:
    return n
  else:
    return fib(n-1) + fib(n-2)
    
func fib2(n: int, a = 0, b = 1): int =
  if n == 0: a else: fib2(n-1, b, a+b)
  
iterator fib3: int =
  var a = 0
  var b = 1
  while true:
    yield a
    swap a, b
    b += a

Factorial

[edit]

Program to calculate the factorial of a positive integer using the iterative approach, showcasing try/catch error handling and for loops:

import std/strutils

var n = 0
try:
  stdout.write "Input positive integer number: "
  n = stdin.readline.parseInt
except ValueError:
  raise newException(ValueError, "You must enter a positive number")

var fact = 1
for i in 2..n:
  fact = fact * i

echo fact

Using the module math from Nim's standard library:

import std/math
echo fac(x)

Reversing a string

[edit]

A simple demonstration showing the implicit result variable and the use of iterators.

proc reverse(s: string): string =
  for i in countdown(s.high, 0):
    result.add s[i]

let str1 = "Reverse This!"
echo "Reversed: ", reverse(str1)

One of Nim's more exotic features is the implicit result variable. Every procedure in Nim with a non-void return type has an implicit result variable that represents the value to be returned. In the for loop we see an invocation of countdown which is an iterator. If an iterator is omitted, the compiler will attempt to use an items iterator, if one is defined for the type specified.

Graphical user interface

[edit]

Using GTK 3 with GObject introspection through the gintro module:

import gintro/[gtk, glib, gobject, gio]

proc appActivate(app: Application) =
  let window = newApplicationWindow(app)
  window.title = "GTK3 application with gobject introspection"
  window.defaultSize = (400, 400)
  showAll(window)

proc main =
  let app = newApplication("org.gtk.example")
  connect(app, "activate", appActivate)
  discard run(app)

main()

This code requires the gintro module to work, which is not part of the standard library. To install the module gintro and many others you can use the tool nimble, which comes as part of Nim. To install the gintro module with nimble you do the following:

nimble install gintro

Programming paradigms

[edit]

Functional programming

[edit]

Functional programming is supported in Nim through first-class functions and code without side effects via the noSideEffect pragma or the func keyword.[70] Nim will perform side effect analysis and raise compiling errors for code that does not obey the contract of producing no side effects when compiled with the experimental feature strictFuncs, planned to become the default in later versions.[71]

Contrary to purely functional programming languages, Nim is a multi-paradigm programming language, so functional programming restrictions are opt-in on a function-by-function basis.

First-class functions

[edit]

Nim supports first-class functions by allowing functions to be stored in variables or passed anonymously as parameters to be invoked by other functions.[72] The std/sugar module provides syntactic sugar for anonymous functions in type declarations and instantiation.

import std/[sequtils, sugar]

let powersOfTwo = @[1, 2, 4, 8, 16, 32, 64, 128, 256]

proc filter[T](s: openArray[T], pred: T -> bool): seq[T] =
  result = newSeq[T]()
  for i in 0 ..< s.len:
    if pred(s[i]):
      result.add(s[i])

echo powersOfTwo.filter(proc (x: int): bool = x > 32)
# syntactic sugar for the above, provided as a macro from std/sugar
echo powersOfTwo.filter(x => x > 32)

proc greaterThan32(x: int): bool = x > 32
echo powersOfTwo.filter(greaterThan32)

Side effects

[edit]

Side effects of functions annotated with the noSideEffect pragma are checked, and the compiler will refuse to compile functions failing to meet those. Side effects in Nim include mutation, global state access or modification, asynchronous code, threaded code, and IO. Mutation of parameters may occur for functions taking parameters of var or ref type: this is expected to fail to compile with the currently-experimental strictFuncs in the future.[73] The func keyword introduces a shortcut for a noSideEffect pragma.[74]

func binarySearch[T](a: openArray[T]; elem: T): int
# is short for...
proc binarySearch[T](a: openArray[T]; elem: T): int {.noSideEffect.}

{.experimental: "strictFuncs".}

type
  Node = ref object
    le, ri: Node
    data: string

func len(n: Node): int =
  # valid: len does not have side effects
  var it = n
  while it != nil:
    inc result
    it = it.ri

func mut(n: Node) =
  let m = n # is the statement that connected the mutation to the parameter
  m.data = "yeah" # the mutation is here
  # Error: 'mut' can have side effects
  # an object reachable from 'n' is potentially mutated

Function composition

[edit]

Uniform function call syntax allows the chaining of arbitrary functions, perhaps best exemplified with the std/sequtils library.[75]

import std/[sequtils, sugar]

let numbers = @[1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2, 1]
# a and b are special identifiers in the foldr macro
echo numbers.filter(x => x > 3).deduplicate.foldr(a + b) # 30

Algebraic data types and pattern matching

[edit]

Nim has support for product types via the object type, and for sum types via object variants: raw representations of tagged unions, with an enumerated type tag that must be safely matched upon before fields of variants can be accessed.[76] These types can be composed algebraically. Structural pattern matching is available, but relegated to macros in various third-party libraries.[77]

import std/tables

type
  Value = uint64
  Ident = string
  ExprKind = enum
    Literal, Variable, Abstraction, Application
  Expr = ref object
    case kind: ExprKind
    of Literal:
      litIdent: Value
    of Variable:
      varIdent: Ident
    of Abstraction:
      paramAbs: Ident
      funcAbs: Expr
    of Application:
      funcApp, argApp: Expr

func eval(expr: Expr, context: var Table[Ident, Value]): Value =
  case expr.kind
  of Literal:
    return expr.litIdent
  of Variable:
    return context[expr.varIdent]
  of Application:
    case expr.funcApp.kind
    of Abstraction:
      context[expr.funcApp.paramAbs] = expr.argApp.eval(context)
      return expr.funcAbs.eval(context)
    else:
      raise newException(ValueError, "Invalid expression!")
  else:
    raise newException(ValueError, "Invalid expression!")

Object-oriented programming

[edit]

Despite being primarily an imperative and functional language, Nim supports various features for enabling object-oriented paradigms.[78][79]

Subtyping and inheritance

[edit]

Nim supports limited inheritance by use of ref objects and the of keyword.[79] To enable inheritance, any initial ("root") object must inherit from RootObj. Inheritance is of limited use within idiomatic Nim code: with the notable exception of Exceptions.[80]

type Animal = ref object of RootObj
  name: string
  age: int
type Dog = ref object of Animal
type Cat = ref object of Animal

var animals: seq[Animal] = @[]
animals.add(Dog(name: "Sparky", age: 10))
animals.add(Cat(name: "Mitten", age: 10))

for a in animals:
  assert a of Animal

Subtyping relations can also be queried with the of keyword.[79]

Method calls and encapsulation

[edit]

Nim's uniform function call syntax enables calling ordinary functions with syntax similar to method call invocations in other programming languages. This is functional for "getters": and Nim also provides syntax for the creation of such "setters" as well. Objects may be made public on a per-field basis, providing for encapsulation.

type Socket* = ref object
  host: int # private, lacks export marker

# getter of host address
proc host*(s: Socket): int = s.host

# setter of host address
proc `host=`*(s: var Socket, value: int) =
  s.host = value

var s: Socket
new s
assert s.host == 0  # same as host(s), s.host()
s.host = 34         # same as `host=`(s, 34)

Dynamic dispatch

[edit]

Static dispatch is preferred, more performant, and standard even among method-looking routines.[79] Nonetheless, if dynamic dispatch is so desired, Nim provides the method keyword for enabling dynamic dispatch on reference types.

import std/strformat

type
  Person = ref object of RootObj
    name: string
  Student = ref object of Person
  Teacher = ref object of Person

method introduce(a: Person) =
  raise newException(CatchableError, "Method without implementation override")

method introduce(a: Student) =
  echo &"I am a student named {a.name}!"

method introduce(a: Teacher) =
  echo &"I am a teacher named {a.name}!"
  
let people: seq[Person] = @[Teacher(name: "Alice"), Student(name: "Bob")]
for person in people:
  person.introduce()

Metaprogramming

[edit]

Templates

[edit]

Nim supports simple substitution on the abstract syntax tree via its templates.

template genType(name, fieldname: untyped, fieldtype: typedesc) =
  type
    name = object
      fieldname: fieldtype

genType(Test, foo, int)

var x = Test(foo: 4566)
echo(x.foo) # 4566

The genType is invoked at compile-time and a Test type is created.

Generics

[edit]

Nim supports both constrained and unconstrained generic programming. Generics may be used in procedures, templates and macros. Unconstrained generic identifiers (T in this example) are defined after the routine's name in square brackets. Constrained generics can be placed on generic identifiers, or directly on parameters.

proc addThese[T](a, b: T): T = a + b
echo addThese(1, 2) # 3 (of int type)
echo addThese(uint8 1, uint8 2) # 3 (of uint8 type)

# we don't want to risk subtracting unsigned numbers!
proc subtractThese[T: SomeSignedInt | float](a, b: T): T = a - b
echo subtractThese(1, 2) # -1 (of int type)

import std/sequtils

# constrained generics can also be directly on the parameters
proc compareThese[T](a, b: string | seq[T]): bool =
  for (i, j) in zip(a, b):
    if i != j:
      return false

One can further clarify which types the procedure will accept by specifying a type class (in the example above, SomeSignedInt).[81]

Macros

[edit]

Macros can rewrite parts of the code at compile-time. Nim macros are powerful and can operate on the abstract syntax tree before or after semantic checking.[82]

Here's a simple example that creates a macro to call code twice:

import std/macros

macro twice(arg: untyped): untyped =
  result = quote do:
    `arg`
    `arg`

twice echo "Hello world!"

The twice macro in this example takes the echo statement in the form of an abstract syntax tree as input. In this example we decided to return this syntax tree without any manipulations applied to it. But we do it twice, hence the name of the macro. The result is that the code gets rewritten by the macro to look like the following code at compile time:

echo "Hello world!"
echo "Hello world!"

Foreign function interface (FFI)

[edit]

Nim's FFI is used to call functions written in the other programming languages that it can compile to. This means that libraries written in C, C++, Objective-C, and JavaScript can be used in the Nim source code. One should be aware that both JavaScript and C, C++, or Objective-C libraries cannot be combined in the same program, as they are not as compatible with JavaScript as they are with each other. Both C++ and Objective-C are based on and compatible with C, but JavaScript is incompatible, as a dynamic, client-side web-based language.[21]: 226 

The following program shows the ease with which external C code can be used directly in Nim.

proc printf(formatstr: cstring) {.header: "<stdio.h>", varargs.}

printf("%s %d\n", "foo", 5)

In this code the printf function is imported into Nim and then used.

Basic example using 'console.log' directly for the JavaScript compiling target:

proc log(args: any) {.importjs: "console.log(@)", varargs.}
log(42, "z", true, 3.14)

The JavaScript code produced by the Nim compiler can be executed with Node.js or a web browser.

Parallelism

[edit]

To activate threading support in Nim, a program should be compiled with --threads:on command line argument. Each thread has a separate garbage collected heap and sharing of memory is restricted, which helps with efficiency and stops race conditions by the threads.

import std/locks

var
  thr: array[0..4, Thread[tuple[a,b: int]]]
  L: Lock

proc threadFunc(interval: tuple[a,b: int]) {.thread.} =
  for i in interval.a..interval.b:
    acquire(L) # lock stdout
    echo i
    release(L)

initLock(L)

for i in 0..high(thr):
  createThread(thr[i], threadFunc, (i*10, i*10+5))
joinThreads(thr)

Nim also has a channels module that simplifies passing data between threads.

import std/os

type
  CalculationTask = object
    id*: int
    data*: int

  CalculationResult = object
    id*: int
    result*: int

var task_queue: Channel[CalculationTask]
var result_queue: Channel[CalculationResult]

proc workerFunc() {.thread.} =
  result_queue.open()

  while true:
    var task = task_queue.recv()
    result_queue.send(CalculationResult(id: task.id, result: task.data * 2))

var workerThread: Thread[void]
createThread(workerThread, workerFunc)

task_queue.open()
task_queue.send(CalculationTask(id: 1, data: 13))
task_queue.send(CalculationTask(id: 2, data: 37))

while true:
  echo "got result: ", repr(result_queue.recv())

Concurrency

[edit]

Asynchronous IO is supported either via the asyncdispatch module in the standard library or the external chronos library.[83] Both libraries add async/await syntax via the macro system, without need for special language support. An example of an asynchronous HTTP server:

import std/[asynchttpserver, asyncdispatch]
# chronos could also be alternatively used in place of asyncdispatch,
# with no other changes.

var server = newAsyncHttpServer()
proc cb(req: Request) {.async.} =
  await req.respond(Http200, "Hello World")

waitFor server.serve(Port(8080), cb)

Community

[edit]

Online

[edit]

Nim has an active community on the self-hosted, self-developed official forum.[84] Further, the project uses a Git repository, bug tracker, RFC tracker, and wiki hosted by GitHub, where the community engages with the language.[85] There are also official online chat rooms, bridged between IRC, Matrix, Discord, Gitter, and Telegram.[86]

Conventions

[edit]

The first Nim conference, NimConf, took place on 20 June 2020. It was held digitally due to COVID-19, with an open call for contributor talks in the form of YouTube videos.[87] The conference began with language overviews by Nim developers Andreas Rumpf and Dominik Picheta. Presentation topics included talks about web frameworks, mobile development, Internet of things (IoT) devices, and game development, including a talk about writing Nim for Game Boy Advance.[88] NimConf 2020 is available as a YouTube playlist.[89] NimConf 2021 occurred the following year, was also held digitally, and included talks about game development, REPLs, real-time operating systems, Nim in the industry, object–relational mapping (ORM), fuzzing, language design, and graphics libraries.[90]

In addition to official conferences, Nim has been featured at various other conventions. A presentation on Nim was given at the O'Reilly Open Source Convention (OSCON) in 2015.[91][92][93] Four speakers represented Nim at FOSDEM 2020, including the creator of the language, Andreas Rumpf.[94] At FOSDEM 2022, Nim hosted their own developer room virtually due to the COVID-19 pandemic.[95] Talks were held on concurrency, embedded programming, programming for GPUs, entity-component systems, game development, rules engines, Python interop, and metaprogramming.[96]

See also

[edit]

References

[edit]
[edit]
Revisions and contributorsEdit on WikipediaRead on Wikipedia
from Grokipedia
Nim is a general-purpose, multi-paradigm, statically typed, compiled language designed for efficiency, expressiveness, and elegance, combining successful concepts from mature languages such as Python, , and . Developed by Andreas Rumpf, Nim was first publicly released in as an experimental project and has since evolved into a mature language with a self-contained and implemented in Nim itself, reaching version 2.2.6 in October 2025. Nim's syntax emphasizes readability through Python-inspired indentation while supporting flexible formatting, and it compiles to C, C++, or JavaScript backends, enabling the generation of native, dependency-free executables for platforms including Windows, , BSD, and macOS, as well as web-based applications. The language features a modern with local , tuples, generics, and sum types; a powerful macro system for via manipulation; and customizable including destructors and move semantics for zero-overhead resource handling. Supporting procedural, object-oriented, and functional paradigms, Nim excels in performance-critical domains through compile-time evaluation, zero-overhead iterators, and stack-allocated value types, making it suitable for embedded systems, hard real-time applications, backend services, and even frontend development via JavaScript compilation. Nim interoperates seamlessly with existing C, C++, Objective-C, and JavaScript codebases through its foreign function interface (FFI), which employs pragmas such as importc and exportc for direct import and export of functions, enabling developers to combine Nim code with these languages' ecosystems and leverage established libraries while benefiting from Nim's concise and expressive syntax; for JavaScript, integration is achieved via the dedicated compilation target.

Introduction

Overview

Nim is a general-purpose, multi-paradigm, statically typed, compiled language designed for efficiency and expressiveness. It compiles to the intermediate languages , , , or , enabling seamless integration with existing ecosystems while producing highly optimized native code via these backends, as well as JavaScript code for web applications. Developed with a focus on elegance and developer productivity, Nim emphasizes clean syntax and powerful capabilities to minimize boilerplate and enhance code readability. By default, Nim employs ORC (Optimized Reference Counting) for automatic memory management, which provides deterministic cleanup through reference counting. ORC extends ARC (Automatic Reference Counting), a compile-time reference counting system that ensures deterministic memory management without runtime cycle collection, making it suitable for real-time applications. Unlike traditional conservative garbage collection, which is still available as an option, ORC includes adaptive cycle detection to handle cyclic data structures while maintaining much of ARC's determinism. Nim also supports manual memory allocation and destruction for performance-critical applications, allowing developers to choose the approach that best fits their needs. The language supports multiple programming paradigms, including procedural, object-oriented, and functional styles, providing flexibility for diverse software development tasks. Nim's primary use cases span systems programming, where its low-level control rivals C; web development through JavaScript backends; embedded systems via cross-compilation targets; and scripting for rapid prototyping. As of November 2025, the current stable version is Nim 2.2.6, released on October 31, 2025.

Design goals

Andreas Rumpf conceived Nim with the ambition to create a language that merges the readability and simplicity reminiscent of Python with the raw performance of , enabling developers to write high-level code that compiles to efficient native binaries without runtime dependencies. This vision prioritizes developer productivity alongside low-level control, addressing the trade-offs often seen in languages that favor either ease of use or speed. The core design goals of Nim, as articulated by its creators, revolve around three pillars: , expressiveness, and , listed in order of priority. is achieved through compilation to optimized , , or code, resulting in minimal runtime overhead and support for deterministic with features like and move semantics, allowing performance comparable to hand-optimized while avoiding garbage collection pauses in critical paths. Expressiveness stems from powerful capabilities, including a macro system for manipulation and compile-time evaluation, which enable concise yet flexible abstractions without sacrificing . manifests in a clean, indentation-based syntax inspired by Python, Ada, and , which minimizes verbosity typically associated with static typing while maintaining clarity and reducing boilerplate. To broaden its appeal, Nim supports multiple programming paradigms, including imperative, procedural, object-oriented, and functional styles through generics and higher-order programming, allowing programmers from diverse backgrounds to adopt it without friction. By compiling to optimized , , , or code, Nim ensures high speed and portability across major platforms like Windows, , macOS, and embedded systems, without compromising the productivity gains from expressive high-level features. This balanced approach avoids the inefficiencies of purely dynamic languages and the cumbersome syntax of many static ones, positioning Nim as a versatile tool for systems, web, and scientific computing.

History

Origins and development

Nim was conceived by Andreas Rumpf in as a personal hobby project aimed at creating an efficient language. Initially named , the project was publicly released that year, with the first implementation written in the language itself. From the outset, Nimrod was designed as an open-source endeavor under the permissive , encouraging community contributions while maintaining Rumpf as the primary architect. The language drew significant inspiration from established paradigms, particularly the modular structure and efficiency of in the Wirthian family, as well as Lisp's metaprogramming capabilities, blending them with influences from Python for readability and Ada for robustness. Early development focused on core features, such as static typing and compilation to C, to achieve high performance without sacrificing expressiveness. Key milestones included the release of version 0.8 in 2011, which introduced substantial improvements in syntax and compiler stability, solidifying its foundation. In response to the negative slang connotation of "Nimrod" as a term for an idiot in American English, stemming from its sarcastic use in Looney Tunes cartoons such as those featuring Bugs Bunny, the language was renamed in December 2014 as part of version 0.10.2. This period saw increasing involvement from a growing team of contributors, transitioning from Rumpf's solo effort to a collaborative open-source project hosted on . The culmination of these foundational efforts arrived with the stable version 1.0 release in September 2019, establishing Nim as a mature language ready for production use.

Major releases and updates

Nim 1.0 was released on September 23, 2019, establishing the core stability of the language after extensive development and community feedback, with no breaking changes to the syntax or semantics introduced in this milestone version. Nim 2.0 arrived on August 1, 2023, as an evolutionary update that made the (Optimized Reference Counting) the default, providing deterministic memory handling without the pauses associated with traditional garbage collection. This release also introduced key improvements in error handling, including experimental definite assignment analysis via the strictDefs pragma to enforce explicit variable initialization, always-enabled strict effects for better callback parameter tracking, and enhanced error messages with detailed type information. Refinements to the ARC/ models added hooks like =wasMoved and =dup for more efficient object management, alongside features such as nested unpacking and top-down . The Nim 2.2 series commenced with version 2.2.0 on October 2, 2024, incorporating nearly 1,000 commits focused on stability, bug fixes for generics and static types, and enhancements to concurrency support through better integration with structured approaches and external libraries. This version improved the backend with for closures and iterators, aiding asynchronous code generation, while also advancing C++ with features like custom constructors. Patch releases followed: 2.2.2 on February 5, 2025, addressing initial stability issues; 2.2.4 on April 22, 2025, with over 100 bug fixes; and 2.2.6 on October 31, 2025, delivering further refinements for enterprise reliability. Ongoing development follows a roadmap emphasizing the evolution of the concurrency model, including the adoption of the Nim Intermediate Representation (NIR) to enable optimizations for multi-threading and async patterns, with a planned transition to formats like NIF for frontend-backend communication by late 2025. Community-driven enhancements continue to prioritize the backend for web concurrency and overall ecosystem stability. In parallel, older features such as the original reference-counting garbage collector have been deprecated in favor of deterministic ARC options, which became the default in to support real-time and performance-critical applications.

Language design

Syntax and semantics

Nim employs an indentation-based syntax that relies on significant whitespace to delineate code blocks and nesting levels, akin to Python's approach for enhanced readability. This means that statements within control structures like conditionals or loops are grouped by consistent indentation levels, typically using spaces rather than tabs, with the lexer tracking indentation via a stack to enforce structural rules. The language's lexis defines identifiers as case-sensitive sequences beginning with a letter or underscore, followed by letters, digits, or underscores, while supporting Unicode characters with ordinal values greater than 127. Keywords, such as if, while, and for, are case-insensitive, allowing variants like If or FOR to function equivalently, but identifiers remain case-sensitive to distinguish user-defined names. To use a keyword as an identifier, Nim supports stropping by enclosing it in backticks, as in let `if` = true. Function calls in Nim feature a uniform syntax that treats method invocations flexibly, permitting both object-oriented notation like obj.method(arg) and procedural style like method(obj, arg). For instance, the length of a string can be obtained via "abc".len or len("abc"), with the compiler resolving the call based on context and type. Semantically, Nim is a statically typed language that employs to deduce types from , reducing explicit annotations while ensuring compile-time . Expressions evaluate to values or l-values (locations that can be assigned to), such as arithmetic operations or conditional expressions like if x > 0: 1 else: 0, which produce a result without side effects unless specified. Statements, in contrast, perform actions like declarations or without yielding values, though statement lists can be wrapped in expressions for use in larger contexts. Control flow constructs follow declarative syntax with colon-separated bodies indented thereafter. The if statement supports conditional execution with optional elif and else branches, as in:

if condition: # indented statements elif otherCondition: # more statements else: # final statements

if condition: # indented statements elif otherCondition: # more statements else: # final statements

The while loop repeats a block until its boolean expression evaluates to false, e.g., while x > 0: x -= 1. The for loop iterates over enumerable expressions, binding identifiers to successive elements, such as for i in 1..5: echo i. Nim allows operator overloading through procedure definitions with special names, enabling user-defined behavior for symbols like + or custom ones, provided they adhere to the language's operator grammar. Operator precedence is stratified across 11 levels, from highest ($ for stringification and ^ for exponentiation) to lowest (logical not), with most operators left-associative and ^-prefixed ones right-associative; parentheses can override this for clarity.

Type system

Nim employs a static where types are resolved and checked at , ensuring and enabling optimizations. The system is primarily structural, meaning type compatibility is determined by the internal structure of types rather than their names, except for specific cases like objects, enumerations, and distinct types which use nominal equivalence. Strong allows the to deduce types from context, such as initializers in variable declarations or return expressions in procedures, minimizing the need for explicit type annotations while maintaining precision. Built-in types in Nim include ordinal types like integers (e.g., int, the platform-dependent natural-sized signed (typically 64 bits on 64-bit platforms), uint for the corresponding unsigned integer, and sized variants from 8 to 64 bits), booleans (bool), characters (char), and enumerations which define ordered sets of identifiers. Floating-point types consist of float (alias for float64), float32, and float64, adhering to standards with built-in overflow and underflow detection. Other primitives are strings (string, a mutable sequence of characters with length prefixing), C-compatible strings (cstring, a null-terminated pointer), sequences (seq[T], resizable arrays), and (tuple, heterogeneous fixed-size collections). Composite types extend these primitives for more complex . Objects (object) define structured records with fields, supporting optional from a root object for relations. Enumerations provide named constants with underlying ordinal values, useful for state representation. Distinct types, declared via distinct, wrap an existing type to create a semantically distinct but structurally related type, preventing implicit conversions for enhanced safety—such as Dollar = distinct int to model separately from general integers. Union types enable a value to hold one of several alternative types, declared using the | operator (e.g., int | float for numeric alternatives), facilitating flexible data representation without runtime overhead in simple cases. For discriminated unions, case objects (also known as variants) use a case statement within an object definition, with a discriminator field (typically an enum) selecting active branches—such as case kind: NodeKind; of nkInt: intVal: int; of nkStr: strVal: string—ensuring type-safe access while inactive fields remain inaccessible at compile time. Type compatibility emphasizes structural equivalence: two types are equal if their components match recursively, allowing interchangeable use of structurally identical types like tuples or arrays. Objects lack nominal subtyping by default, relying instead on explicit for subtype polymorphism, where a type A is a subtype of B if A from B directly or indirectly. Convertible relations support implicit widening (e.g., int8 to int) and explicit casts, with distinct types requiring deliberate conversion to their base. Error handling integrates with the through exceptions, which are objects inheriting from Exception or Defect (for uncatchable errors like overflows), raised at runtime for conditions such as index out-of-bounds (IndexDefect) or arithmetic overflows (OverflowDefect). Additionally, result types like Result[T, E]—a common idiom often implemented as a discriminated union or —encapsulate success values of type T alongside potential errors E, promoting functional-style error propagation without exceptions.

Metaprogramming features

Nim's metaprogramming capabilities enable developers to generate and manipulate code at compile-time, facilitating powerful abstractions and without runtime overhead. These features, including generics, templates, macros, and concepts, allow the language to be extended dynamically while maintaining static typing and efficiency. By operating on the (AST) or through parameterization, they support the creation of domain-specific languages and optimized code generation, distinguishing Nim from languages with limited compile-time introspection. Generics in Nim provide parameterized types and procedures, enabling type-safe polymorphism and reusable code across different types. They are defined using square brackets for type parameters, such as proc add[T](a, b: T): T, where T is resolved at compile-time during overload resolution. This mechanism ensures that the generated code is specialized for each type, avoiding boxing or dynamic dispatch, and supports advanced features like aliasing type parameters for efficiency. For instance, a generic sequence type like seq[T] can handle any element type while preserving performance comparable to native arrays. Templates offer a simple, unhygienic form of code generation through textual substitution at compile-time, ideal for boilerplate reduction and basic . Declared with the template keyword, they use untyped parameters to delay type checking until expansion, as in template additive(typ: typedesc) = proc +*(x, y: typ): typ {.borrow.}. This expands to define operator overloads for custom types, borrowing implementations from built-in types. Templates are particularly useful in the for features like asynchronous programming, where they generate efficient state machines without runtime costs. Unlike more complex tools, templates prioritize simplicity over full AST control, making them suitable for straightforward reuse. Macros extend by allowing hygienic manipulation of the AST, enabling sophisticated code transformations during compilation. Defined with the macro keyword, they take typed or untyped parameters and return modified AST nodes, such as generating an enum from a :

nim

import macros, strutils macro toEnum(words: static[string]): untyped = result = newTree(nnkEnumTy, newEmptyNode()) for w in splitWhitespace(words): result.add ident(w) type Color = toEnum"Red Green Blue Indigo"

import macros, strutils macro toEnum(words: static[string]): untyped = result = newTree(nnkEnumTy, newEmptyNode()) for w in splitWhitespace(words): result.add ident(w) type Color = toEnum"Red Green Blue Indigo"

This produces type Color = enum Red, Green, Blue, Indigo, demonstrating runtime-like at compile-time. Macros are type-aware and support reflection via nodes like ident or newTree, but require careful handling to avoid infinite or issues. They power advanced libraries by creating custom syntax while ensuring the final code compiles to efficient . Concepts introduce compile-time type classes to constrain generics, ensuring that parameterized code only applies to types meeting specific requirements. Declared with the concept keyword, they specify operations or properties, e.g., concept Comparable = concept var t: Self; proc <(a, b: Self): bool. This can then be used in generics like proc sort[T: Comparable](x: seq[T]), enabling compile-time checks for compatibility and better error messages. Concepts facilitate duck typing within a static framework, improving code safety and expressiveness without runtime penalties, and integrate seamlessly with overload resolution for precise matching. Compile-time evaluation and reflection further enhance metaprogramming by executing expressions during compilation and inspecting code structure. Using static blocks or parameters, such as static: echo "Computed at compile-time", Nim evaluates constants or generates values like Fibonacci sequences in const fib = static(nextFib()). Reflection occurs primarily through macros accessing AST elements via typeof or node inspection, allowing dynamic code analysis. These features are restricted—no foreign function interfaces, closures, or reference types—to ensure safety, and they interleave with semantic analysis for verified results. Unlike runtime reflection in languages like Python, all operations resolve fully before transpilation to C, yielding zero-cost abstractions. In contrast to runtime features like dynamic typing or virtual methods, Nim's metaprogramming occurs entirely at compile-time, producing specialized, efficient machine code without indirection or interpretation overhead. This design ensures that abstractions like generics or macros vanish in the final binary, aligning with Nim's goals of performance and elegance.

Programming paradigms

Imperative and procedural

Nim supports imperative programming through its core syntax for mutable state, control flow, and procedural abstractions, enabling developers to write straightforward, step-by-step code similar to languages like C or Pascal. Procedures, known as "procs," serve as the primary building blocks for organizing code into reusable functions, emphasizing explicit control over execution and data modification. Procedures are declared using the proc keyword, followed by the procedure name, a parameter list in parentheses, an optional return type after a colon, and the body. For example, a simple addition procedure is written as proc add(a, b: int): int = a + b, where a and b are parameters typed as integers, and the return type is int. Parameters can include defaults, such as proc greet(name: string = "World"): string = "Hello, " & name, and support var for pass-by-reference modification, like proc increment(x: var int) = x += 1. Return values are assigned via an implicit result variable or the return statement, with the compiler ensuring type safety. Mutability is managed explicitly through var for variables that can be reassigned and let for constants that remain immutable after initialization. A mutable integer is declared as var count: int = 0; count += 1, allowing changes, whereas let pi: float = 3.14 prevents reassignment to enforce safer code. This distinction aids in tracking state changes in imperative code. Nim's effects system further refines this by requiring annotations on procedures to declare potential side effects, such as I/O operations or exceptions, which enables compiler optimizations like inlining pure functions. For instance, proc readInput(): string {.tags: [IoEffect], raises: [IOError].} = ... specifies I/O involvement and possible errors, while {.noSideEffect.} marks procedures without external effects, such as proc square(x: int): int {.noSideEffect.} = x * x. Core imperative control structures include loops and conditionals for directing program flow. The while loop executes a block as long as a condition holds, e.g., var i = 0; while i < 5: i += 1, and the for loop iterates over ranges or collections, like for j in 1..3: echo j. Conditionals use if for branching, such as if x > 0: echo "Positive" else: echo "Non-positive", and case for multi-way decisions, e.g., case day: of "Monday": echo "Start of week". Iterators, declared with the iterator keyword, provide a procedural way to generate sequences on-the-fly, using yield to produce values lazily; for example, iterator countdown(n: int): int = while n > 0: yield n; n -= 1, which can be consumed in a for loop. For low-level control, Nim offers options alongside its default garbage collection. The new procedure allocates heap memory for types, initializing them, as in var node: ref Node; new(node); node.value = 42. Conversely, dispose explicitly frees untraced or manual allocations, such as dispose(pointer), providing fine-grained control for performance-critical imperative code.

Object-oriented

Nim's object-oriented paradigm builds upon its record types, extending them to support , method attachment, and for objects. Objects are defined similarly to records but provide additional capabilities, such as hiding fields from other modules and enabling hierarchies. Methods are attached to objects using the method keyword, allowing for object-specific procedures that can be invoked via dot notation, such as obj.methodName(args). This design integrates seamlessly with Nim's , where objects serve as value types on the stack by default. For dynamic allocation and reference semantics, Nim uses ref object types, which allocate instances on the heap via the built-in new procedure. For example:

nim

type Node = ref object data: int next: Node var n: Node new(n) n.data = 42

type Node = ref object data: int next: Node var n: Node new(n) n.data = 42

This approach ensures garbage collection compatibility while maintaining explicit control over object lifetimes. Ref objects also enable runtime polymorphism, as they carry type information for dynamic dispatch. Inheritance in Nim supports single inheritance exclusively, defined using the object of syntax, which establishes an is-a relationship and implicit . A child object inherits all fields and methods from its , and the child becomes a subtype of the parent, allowing it to be used wherever the parent type is expected. For instance:

nim

type [Animal](/page/Animal) = object of RootObj name: string [Dog](/page/Dog) = object of [Animal](/page/Animal) breed: string

type [Animal](/page/Animal) = object of RootObj name: string [Dog](/page/Dog) = object of [Animal](/page/Animal) breed: string

To achieve effects similar to without direct support—which is omitted to prevent problem complexities—programmers use mixins for selective method importation or composition for embedding multiple object instances. remains implicit and covariant within this single-inheritance chain, ensuring without explicit casts. Method dispatch operates statically by default for performance, resolving calls at based on the static type. However, for ref object types, the method keyword enables dynamic (virtual) dispatch, where the actual method invoked is determined at runtime based on the object's dynamic type. This supports polymorphism in hierarchies, such as overriding a parent's method in a object. Encapsulation is enforced through module-level visibility: object fields are private by default and inaccessible outside the defining module, but can be exported publicly by appending an (*) to the field name, e.g., publicField*: int. This explicit visibility control promotes while avoiding accidental exposure of internal state.

Functional

Nim supports functional programming through first-class functions, which allow procedures to be treated as values that can be assigned to variables, passed as arguments to other procedures, or returned from them. This enables composable code where functions serve as building blocks for higher-level abstractions. Procedures are defined using the proc keyword, and their first-class nature is inherent to Nim's for procedural types. Anonymous procedures, also known as lambdas, can be defined inline without a name, facilitating concise expressions for one-off functions. For example:

nim

let add = proc (x, y: int): int = x + y echo add(3, 4) # Outputs: 7

let add = proc (x, y: int): int = x + y echo add(3, 4) # Outputs: 7

These anonymous procs support closures, which capture variables from their enclosing scope by reference, using the .closure. calling convention. This allows nested procedures to access and potentially modify outer variables, promoting flexible function creation. Partial application is achievable through higher-order functions and closures, where a function can be wrapped to fix some arguments, though Nim lacks built-in currying syntax. Immutability is encouraged in Nim to foster predictable, side-effect-free code. The let statement declares immutable variables that cannot be reassigned after initialization, ensuring values remain constant throughout their scope. For procedures, the func keyword defines pure functions that implicitly apply the {.noSideEffect.} pragma, guaranteeing no modifications to external state and dependence solely on input arguments. This is equivalent to declaring a proc with {.noSideEffect.}, enabling optimizations and functional purity. For instance:

nim

func add(a, b: int): int = a + b # Pure function, no side effects

func add(a, b: int): int = a + b # Pure function, no side effects

Such pure functions align with functional principles by producing outputs deterministically from inputs alone. Function composition in Nim is supported through pipelining and the do notation, allowing chained transformations for readable, declarative code. The pipe operator |> (available via the sugar module or custom definition) passes the result of an expression as the first argument to the next function, enabling fluid pipelines. The do notation provides a syntactic sugar for supplying anonymous procedures to higher-order functions inline. An example of pipelining:

nim

proc double(x: int): int = x * 2 echo 5 |> double # Outputs: 10

proc double(x: int): int = x * 2 echo 5 |> double # Outputs: 10

Using do with sorting:

nim

proc sort[T](s: var seq[T], cmp: proc (x, y: T): int) = ... var cities = @["London", "Tokyo", "Paris"] sort(cities) do (x, y: string) -> int: cmp(x.len, y.len)

proc sort[T](s: var seq[T], cmp: proc (x, y: T): int) = ... var cities = @["London", "Tokyo", "Paris"] sort(cities) do (x, y: string) -> int: cmp(x.len, y.len)

This composes functions by treating data flows as pipelines, reducing nesting and improving clarity. Nim provides algebraic data types through object variants, which implement sum types as discriminated unions. These allow a single type to hold values of different subtypes based on a tag, typically an enum discriminator. For example:

nim

type NodeKind = enum nkInt, nkFloat Node = ref object case kind: NodeKind of nkInt: intVal: int of nkFloat: floatVal: float

type NodeKind = enum nkInt, nkFloat Node = ref object case kind: NodeKind of nkInt: intVal: int of nkFloat: floatVal: float

on these variants is performed using case statements, which branch based on the discriminator for exhaustive or selective handling. Continuing the example:

nim

proc printNode(n: Node) = case n.kind of nkInt: echo n.intVal of nkFloat: echo n.floatVal

proc printNode(n: Node) = case n.kind of nkInt: echo n.intVal of nkFloat: echo n.floatVal

This construct supports safe, expressive data modeling akin to variants in other functional languages, with the compiler aiding in discriminant checks. The enhances with higher-order functions like map and filter in the sequtils module, operating on sequences for transformations and selections. The map function applies a given procedure to each element, producing a new sequence:

nim

import sequtils let nums = @[1, 2, 3] let doubled = nums.map(proc (x: int): int = x * 2) # @[2, 4, 6]

import sequtils let nums = @[1, 2, 3] let doubled = nums.map(proc (x: int): int = x * 2) # @[2, 4, 6]

Similarly, filter retains elements satisfying a predicate:

nim

let evens = nums.filter(proc (x: int): bool = (x mod 2) == 0) # @[2]

let evens = nums.filter(proc (x: int): bool = (x mod 2) == 0) # @[2]

These iterators promote immutable and can integrate with do notation for brevity, such as nums.filter do (x: int) -> bool: x > 1. They facilitate composable operations on collections without mutating originals.

Concurrent and parallel

Nim provides support for concurrency and parallelism through a of low-level and higher-level abstractions, emphasizing safety in access. The language's threads module enables OS-level threading. This allows creation of explicit threads via createThread, facilitating concurrent execution of independent tasks. For lightweight task parallelism, Nim offers the spawn primitive from the threadpool module, which schedules computations on a without blocking the caller, returning a FlowVar for and result retrieval. As of 2025, the standard threadpool has been deprecated in favor of community-maintained alternatives like , taskpools, or weave for more robust parallel execution, addressing performance regressions observed in Nim 2.0. Cooperative concurrency is handled via the asyncdispatch module, which implements an event-driven dispatcher supporting async/await syntax for non-blocking I/O operations. This model relies on futures for deferred computations and channels for inter-task communication, enabling efficient handling of asynchronous events without OS threads. The select statement in the channels module further supports , allowing a single thread to wait on multiple channels or async events and react to the first available one, similar to select in Unix systems. Parallelism can also be achieved using the parallel statement, which transforms loops or blocks into parallel executions, generating directives when compiling to for hardware-accelerated parallelism on multi-core systems. Manual thread pools provide finer control for custom parallel workloads. Nim employs a shared memory model where global variables and explicitly allocated shared objects reside on a process-wide heap, accessible across threads via allocShared. To prevent data races, mutability is managed carefully with primitives: the atomics module offers lock-free operations like load, store, exchange, and fetchAdd with configurable memory orders (e.g., moAcquireRelease), while the locks module provides mutexes (Lock) and condition variables (Cond) for guarded access, including the withLock template for scoped acquisition. Recent enhancements in Nim 2.0 include shared heap support under the memory manager, with thread safety for shared achieved through explicit using locks and atomic operations. In multi-threaded contexts, Nim's integrates atomic operations to handle shared references, ensuring deterministic destruction without leaks in concurrent scenarios.

Compiler and runtime

Compilation targets and process

The Nim compiler processes source code through a multi-stage that begins with and to construct an (AST), followed by semantic analysis for type checking and resolution, and concludes with backend-specific code generation. This ensures that Nim programs are transformed into intermediate representations suitable for various execution environments, emphasizing efficiency and portability. The primary backend generates C code, which is then compiled into native executables using external compilers such as GCC, , or MSVC, allowing Nim to produce high-performance binaries for desktop and server applications. Alternative targets include for web-based execution in browsers, enabling client-side scripting with Nim's syntax, and for iOS and macOS app development, facilitating integration with Apple's ecosystem. Other backends, such as C++, support specialized use cases like embedding in C++ projects. Compiler flags provide fine-grained control over the build process; for instance, --opt:speed enables aggressive optimizations to prioritize runtime performance, while --mm:arc selects the Automatic memory model. Cross-compilation is robustly supported across platforms including Windows, , macOS, and embedded systems, achieved via options like --os:linux and --cpu:arm to target diverse architectures without a host for the destination. In debug modes, hot code reloading is available via --hotCodeReloading:on, allowing runtime updates to code without full restarts, particularly useful for interactive prototyping.

Memory management

Nim employs a flexible memory management system that supports multiple strategies, allowing developers to select approaches suited to performance, determinism, and resource constraints via the --mm: compiler flag. The default strategy, introduced as the standard in Nim 2.0, is ORC (Optimized Reference Counting), which can be enabled via --mm:orc, a deterministic reference counting system with integrated cycle detection to handle circular references without garbage collection pauses. ORC maintains a shared heap across threads, enabling efficient multi-threaded operation while ensuring predictable deallocation at scope exit. An alternative Boehm garbage collector, which uses a conservative, stop-the-world mark-and-sweep algorithm on a shared heap, remains available as an optional choice for scenarios requiring simpler integration with legacy code. ARC (Automatic Reference Counting), a variant of ORC without cycle detection, offers even lower overhead for acyclic data structures and can be enabled via --mm:arc; it prioritizes minimal latency but may leak memory in the presence of cycles, such as those introduced by asynchronous code. Both ARC and ORC, first experimentally available in Nim 1.4 and fully matured in 2.0, replace traditional garbage collection with compile-time reference tracking, ensuring deallocation occurs immediately when reference counts reach zero. These models integrate destructors—procedures invoked via the =destroy hook—to automatically release non-memory resources like file handles or sockets upon object destruction. For fine-grained control, Nim supports manual memory management through untraced pointers declared with the ptr type, which bypass automatic tracking. Developers can allocate heap memory using alloc or realloc from the system module and explicitly free it with dealloc, while new initializes traced reference objects on the heap (though deallocation is typically GC-handled unless --mm:none disables the collector entirely). The dispose procedure provides a manual way to invoke destructors and free traced objects early. Mixing manual and automatic management requires caution, such as using reset to clear GC-tracked fields before deallocating containing pointers, to prevent leaks or invalid accesses. To optimize performance under ARC/ORC, Nim implements sinking optimization through move semantics, which transfers ownership of resources without unnecessary copies. This is facilitated by the =sink hook, allowing procedures to "steal" contents from source objects (marked as moved via =wasMoved) and leave them in a valid but empty state. For example, sinking a sequence avoids duplicating its underlying buffer:

nim

type MySeq = object data: seq[int] proc `=sink`(a: var MySeq; b: MySeq) =

type MySeq = object data: seq[int] proc `=sink`(a: var MySeq; b: MySeq) =

Transfer ownership without copy

shallow(a., b.) # Or similar resource move

Mark b as moved if needed

var s1 = MySeq(: @[1, 2, 3]) var s2: MySeq s2 = s1 # Sinks: no copy of

This reduces allocation overhead in functional-style code or when returning large objects.[](https://nim-lang.org/docs/destructors.html#move-semantics) In concurrent programming, memory models like the legacy `--mm:refc` (deferred [reference counting](/page/Reference_counting) with mark-and-sweep backup) and `--mm:markAndSweep` utilize thread-local heaps to minimize contention and enable independent collection per thread, though this complicates [data sharing](/page/Data_sharing) across threads.[](https://nim-lang.org/docs/mm.html)[](https://nim-lang.org/docs/refc.html) ORC and ARC, by contrast, employ a shared heap with atomic [reference counting](/page/Reference_counting) for thread safety, avoiding per-thread isolation while preserving determinism.[](https://nim-lang.org/docs/mm.html) The ARC/ORC models trade traditional garbage collection's simplicity for enhanced control, offering sub-millisecond pauses suitable for real-time systems but necessitating explicit [destructors](/page/The_Destructors) for resource cleanup to prevent leaks in non-memory contexts.[](https://nim-lang.org/docs/mm.html)[](https://nim-lang.org/docs/destructors.html) In exchange, they eliminate stop-the-world events inherent in Boehm-style collectors, at the cost of slightly higher compile-time analysis for [cycle detection](/page/Cycle_detection) in ORC.[](https://nim-lang.org/docs/mm.html) Disabling the collector entirely (`--mm:none`) grants maximum performance for embedded or systems code but demands rigorous manual oversight to avoid memory exhaustion.[](https://nim-lang.org/docs/mm.html) ### Foreign function interface Nim's foreign function interface (FFI) enables seamless [interoperability](/page/Interoperability) with code written in other languages, primarily through its [C](/page/C) backend, allowing developers to leverage existing libraries without significant performance overhead. The FFI is centered on direct integration with [C](/page/C), where Nim procedures and variables can be marked for import using the `importc` pragma, which declares them as external entities from [C](/page/C) code. For instance, to call the standard [C](/page/C) `printf` function, one defines: ```nim proc printf(formatstr: cstring; args: varargs[pointer]) {.importc: "printf", varargs, header: "<stdio.h>".}

This reduces allocation overhead in functional-style code or when returning large objects.[](https://nim-lang.org/docs/destructors.html#move-semantics) In concurrent programming, memory models like the legacy `--mm:refc` (deferred [reference counting](/page/Reference_counting) with mark-and-sweep backup) and `--mm:markAndSweep` utilize thread-local heaps to minimize contention and enable independent collection per thread, though this complicates [data sharing](/page/Data_sharing) across threads.[](https://nim-lang.org/docs/mm.html)[](https://nim-lang.org/docs/refc.html) ORC and ARC, by contrast, employ a shared heap with atomic [reference counting](/page/Reference_counting) for thread safety, avoiding per-thread isolation while preserving determinism.[](https://nim-lang.org/docs/mm.html) The ARC/ORC models trade traditional garbage collection's simplicity for enhanced control, offering sub-millisecond pauses suitable for real-time systems but necessitating explicit [destructors](/page/The_Destructors) for resource cleanup to prevent leaks in non-memory contexts.[](https://nim-lang.org/docs/mm.html)[](https://nim-lang.org/docs/destructors.html) In exchange, they eliminate stop-the-world events inherent in Boehm-style collectors, at the cost of slightly higher compile-time analysis for [cycle detection](/page/Cycle_detection) in ORC.[](https://nim-lang.org/docs/mm.html) Disabling the collector entirely (`--mm:none`) grants maximum performance for embedded or systems code but demands rigorous manual oversight to avoid memory exhaustion.[](https://nim-lang.org/docs/mm.html) ### Foreign function interface Nim's foreign function interface (FFI) enables seamless [interoperability](/page/Interoperability) with code written in other languages, primarily through its [C](/page/C) backend, allowing developers to leverage existing libraries without significant performance overhead. The FFI is centered on direct integration with [C](/page/C), where Nim procedures and variables can be marked for import using the `importc` pragma, which declares them as external entities from [C](/page/C) code. For instance, to call the standard [C](/page/C) `printf` function, one defines: ```nim proc printf(formatstr: cstring; args: varargs[pointer]) {.importc: "printf", varargs, header: "<stdio.h>".}

This setup permits Nim code to invoke functions as if they were native, with the generating appropriate wrappers. Type mapping between Nim and C is straightforward, with Nim providing built-in types that correspond to C primitives for compatibility. Common mappings include cint for C's int, cchar for char, pointer for generic pointers, and cstring for null-terminated C strings (equivalent to char*). The cstring type is particularly useful but requires caution, as it lacks automatic memory management and can lead to issues if Nim strings are converted implicitly without ensuring zero-termination. More complex C structures, such as arrays or unions, map to Nim's array, seq, or object types, often requiring manual adjustment for full fidelity. Calling conventions are specified via pragmas to match those of the foreign code, ensuring correct argument passing and stack cleanup. The cdecl pragma adopts the standard convention, where the caller handles stack cleanup, suitable for most systems; under Windows, it generates __cdecl declarations. For compatibility, stdcall uses the callee-cleaned stack model, producing __stdcall. These are combined with importc, as in:

nim

proc messageBox(hWnd: pointer; text, caption: cstring; uType: uint32): cint {.importc: "MessageBoxA", stdcall, header: "<windows.h>".}

proc messageBox(hWnd: pointer; text, caption: cstring; uType: uint32): cint {.importc: "MessageBoxA", stdcall, header: "<windows.h>".}

By default, imported functions assume cdecl unless overridden. To automate wrapper generation from C headers, Nim includes the official c2nim tool, which translates code into readable Nim equivalents for manual refinement. It processes header files, handling defines, structs, and enums—e.g., converting #define MAX 100 to const MAX* = 100—while including a for complex inputs. Installation via Nimble (nimble install c2nim) enables quick prototyping of FFI bindings, though users often tweak the output for Nim idioms like generics or safety. Beyond C, Nim supports other languages through intermediaries or direct pragmas. For C++, the importcpp pragma allows importing classes, templates, and functions, using placeholders like @ for arguments:

nim

type Vector[T] {.importcpp: "std::vector<'0>", header: "<vector>".} = object proc newVector[T](): Vector[T] {.importcpp: "std::vector<'0>(@)".}

type Vector[T] {.importcpp: "std::vector<'0>", header: "<vector>".} = object proc newVector[T](): Vector[T] {.importcpp: "std::vector<'0>(@)".}

This facilitates reuse of components, such as std::vector, with support for constructors and destructors. For languages like Python, interop occurs via C's Python API using libraries such as nimpy, which provides bidirectional bindings by embedding Python interpreters or extending Python with Nim modules. interop leverages Nim's JS backend directly. These approaches rely on C as a bridge where native support is absent. Safety in FFI calls emphasizes developer responsibility, as Nim's runtime protections like garbage collection do not extend to foreign code. Bounds checking, enabled by default for Nim arrays and sequences, can be disabled globally via --boundChecks:off or per-procedure with the noBoundsCheck pragma to match C's unchecked access, reducing overhead but risking buffer overflows. The cstring type explicitly omits bounds checking during indexing to mimic C behavior, and pointers in FFI require manual null checks to avoid crashes. Pragmas like gcsafe ensure foreign procedures do not interact unexpectedly with Nim's collector.

Development tools

Bundled tools

The Nim programming language distribution includes a suite of bundled tools designed to support core development tasks, such as package management, code analysis, formatting, testing, and integration with external codebases. These utilities are provided alongside the compiler to streamline workflows for Nim developers, ensuring portability and consistency without reliance on external dependencies. Nimble serves as the default package manager for Nim, enabling users to search for packages, install dependencies, create new projects, and upload libraries to the official repository at nimble.directory. It is integrated into the Nim installation and requires for handling repository-based packages. Atlas is a simple package cloner tool that manages project dependencies in an isolated deps/ directory, compatible with Nimble for handling workspaces and dependencies. c2nim is a converter tool that translates C and C++ header files into equivalent Nim modules, facilitating interoperability with existing C libraries by generating Nim wrappers. This allows developers to bind to C code while maintaining Nim's type safety and syntax. koch acts as the primary maintenance script for the Nim project, used to build the compiler, generate documentation, and perform other build-related tasks as a portable alternative to traditional makefiles or shell scripts. It supports commands for bootstrapping, testing, and website generation within the Nim repository. nimgrep provides a grep-like utility tailored for searching and replacing patterns within Nim source code files, supporting regular expressions and handling Nim-specific syntax for efficient code navigation and refactoring. nimsuggest enables IDE integration by querying Nim source files to deliver information such as symbol definitions, autocompletion suggestions, and navigation aids, allowing editors to offer real-time development support. It operates as a server process that responds to protocol-based requests from compatible tools. niminst generates installers for Nim programs and packages, producing self-contained executables or scripts suitable for distribution across platforms like Windows, , and macOS. It handles dependency resolution and packaging to simplify deployment. nimpretty is a source code beautifier that automatically formats Nim code according to the official , promoting consistent readability and adherence to community conventions during development and maintenance. Testament functions as an advanced testing framework for Nim, running unit tests with features like process isolation, support for multiple compilation targets (e.g., C, JavaScript), detailed logging, HTML report generation, and conditional test skipping based on configuration. It integrates seamlessly with Nim's module system for comprehensive validation of codebases.

Third-party tools

The Nim ecosystem benefits from a variety of community-developed tools that enhance productivity and integration capabilities beyond the standard compiler distribution. These third-party tools address specific needs such as version management, language bridging, and automated code generation, fostering a flexible development environment for Nim users. Choosenim serves as a version manager that simplifies the installation and switching between multiple releases, including stable and development versions sourced directly from official downloads. It allows developers to maintain isolated environments for different projects, preventing compatibility issues during upgrades or experimentation with bleeding-edge features. By automating the download, compilation, and activation of versions via a simple , choosenim streamlines workflow in multi-version setups. Nimpy provides a robust bridge for integrating Python code within Nim programs or exposing Nim functionality to Python scripts, enabling seamless data exchange between the two languages. It handles type conversions, such as transforming Nim strings into Python-compatible formats while managing encoding challenges like invalid sequences by falling back to byte strings. This tool is particularly useful for leveraging Python's extensive libraries in Nim applications or vice versa, with support for embedding Python interpreters directly in Nim binaries. Pixie is a comprehensive 2D graphics library that includes tooling for image processing and vector operations, akin to established libraries like Cairo and Skia. It supports rasterization tasks such as cropping, shape drawing, and text rendering, all implemented natively in Nim for high performance without external dependencies. Developers can use Pixie's utilities to build graphical applications, prototypes, or utilities for image manipulation, with ongoing active development ensuring compatibility with evolving Nim features. Nimterop automates the generation of wrappers for C and C++ libraries, making foreign function interface (FFI) integration more efficient by parsing header files with the tree-sitter engine. It converts relevant abstract syntax tree (AST) portions from C/C++ into Nim-compatible definitions, reducing manual boilerplate and error-prone header translations. This tool excels in large-scale interop scenarios, such as binding complex C++ APIs, and supports customizable output to align with Nim's type system. Arraymancer offers a toolkit for numerical , featuring tensor operations with support for acceleration on CPU, CUDA, and backends. It provides ergonomic abstractions for multidimensional arrays, enabling efficient data processing in scientific and contexts without sacrificing Nim's performance characteristics. The library's tooling includes utilities for tensor manipulation, linear algebra, and optimization routines, making it suitable for high-throughput computations. Jester functions as a lightweight with integrated server tooling, modeled after Sinatra to facilitate rapid development of HTTP-based applications in . It employs a (DSL) for defining routes, handling requests, and managing responses, including built-in support for and templating. This setup allows developers to deploy full web servers with minimal configuration, ideal for APIs, , or prototyping. Nimporter is an advanced tool for Python-Nim integration, building on Nimpy to compile and import Nim extensions dynamically during Python module loading. It addresses packaging challenges by automating the build process for Nim code as Python extensions, enabling hybrid applications to leverage Nim's speed within Python ecosystems seamlessly. This update enhances cross-language deployment, particularly for data-intensive projects requiring low-level optimizations.

Libraries and ecosystem

Standard library

Nim's standard library comprises a collection of modules that provide essential functionality for general-purpose programming, categorized into pure libraries, impure libraries, and wrappers. Pure libraries are self-contained and do not rely on external binaries such as DLLs or shared objects, ensuring portability and predictability; examples include modules for string handling and mathematical operations. Impure libraries, in contrast, depend on system-specific binaries for tasks involving or external integrations, while wrappers offer low-level interfaces to C libraries for advanced system access. This division allows developers to select modules based on the need for purity in functional paradigms versus practical side effects in imperative code. Core modules form the foundation of Nim programming. The system module, implicitly imported in every Nim file, supplies built-in procedures, types, and constants essential for basic operations, such as type conversions and runtime checks. The strutils module delivers a suite of string manipulation functions, including splitting, joining, and formatting, optimized for efficiency in text processing. Complementing sequences—Nim's dynamic arrays—the sequtils module offers higher-order functions like mapping, filtering, and folding, enabling concise functional-style programming on collections. For numerical computations, the math module provides constants (e.g., π, e), , and statistical utilities, supporting both and with high precision. Input/output and operating system interactions are handled by dedicated modules in the impure category. The os module exposes cross-platform facilities for file and directory operations, environment variables, and process management, abstracting POSIX and Windows APIs to simplify system-level programming. The streams module implements abstract I/O streams for reading and writing data in a buffered, sequential manner, compatible with both files and in-memory sources. Networking capabilities are supported by the httpclient module, which facilitates synchronous and asynchronous HTTP requests, including support for headers, proxies, and SSL/TLS, making it suitable for web-based applications. Data structures in the emphasize efficiency and generality. The tables module implements ordered and unordered hash tables for key-value storage, with features like automatic resizing and collision resolution to handle large datasets. The sets module provides ordered sets with operations such as union, , and difference, leveraging hash tables for O(1) average-time complexity. For queue-like operations, the deques module offers double-ended queues that support efficient appends and pops from both ends, ideal for breadth-first searches or task scheduling. These structures are pure where possible, promoting without side effects. The distinction between pure and impure modules underscores Nim's support for side-effect-free programming. Pure modules, such as strutils, sequtils, and math, avoid I/O operations and global state modifications, aligning with principles by ensuring and . Impure modules, like os and streams, incorporate side effects for real-world interactions, such as file access or network calls, but are designed to be contained within delimited scopes to minimize risks in concurrent environments. This separation enables developers to mix paradigms while maintaining code reliability. Concurrency and asynchronous programming are addressed through specialized modules. The threads module enables low-level multithreading with primitives for thread creation, synchronization via locks and conditions, and access, built on platform-native threading libraries. For event-driven I/O, asyncdispatch provides a for asynchronous procedures, integrating with selectors for efficient polling of file descriptors and sockets. The channels module facilitates safe communication between threads or async tasks using buffered or unbuffered channels, inspired by Go's but adapted to Nim's memory model for transfers where possible. These modules are impure due to their reliance on OS threading and I/O primitives. Recent versions of Nim have expanded the 's capabilities. The includes hashing modules such as md5 and sha1 under the hashes category for secure hashing primitives, available since earlier versions; advanced encryption like AES is supported via external libraries. The json module received significant improvements in version 2.2 (released in ), featuring a high-performance parser and builder for handling large JSON datasets with reduced and faster serialization. Nim 2.2.6, released in October 2025, includes further stability improvements but no major new stdlib modules. These additions reflect ongoing efforts to bolster Nim's utility for secure and data-intensive applications without external dependencies.

Package management and external libraries

Nimble serves as the primary package manager for the Nim programming language, facilitating the discovery, installation, and management of third-party libraries and dependencies. Developers can install packages using the nimble install <package> command, which downloads and compiles the specified library from repositories like GitHub, while nimble develop <package> enables local development by symlinking the package source for editing without full installation. This tool integrates seamlessly with Nim's build process, automatically resolving transitive dependencies—those required by installed packages—and supporting version pinning in configuration files to lock specific library versions for reproducibility. Build configurations for Nim projects, including dependency management, are often defined using NimScript in files named build.nims. NimScript, a subset of the Nim interpreted by the compiler's , allows declarative setup of compilation flags, tasks, and hooks, such as pre- or post-build actions, without requiring a full Nim compilation for the build script itself. This approach provides flexibility for complex projects while maintaining compatibility with Nimble's dependency resolution. External libraries in Nim are categorized as pure or impure, mirroring the standard library's distinctions: pure libraries are implemented entirely in Nim without relying on external binaries or foreign code, ensuring portability across compilation targets, whereas impure libraries interface with system-level dependencies like C libraries via Nim's foreign function interface. For example, Jester is a popular pure web framework inspired by Sinatra, offering a domain-specific language for routing and handling HTTP requests in server-side applications. Karax provides a framework for building single-page JavaScript frontends directly in Nim, compiling to efficient browser code. Chronos, an asynchronous programming library, supports event loops and futures for concurrent I/O operations, often used in networked applications. Packages are discovered through the official Nim Package Directory, which indexes over 2,500 libraries as of 2025, as well as repositories under the nim-lang organization and discussions on the Nim forum. The 2024 Nim community survey highlighted ecosystem growth, with 28% of respondents using Nim for over 60% of their work (up from 20% in 2023) and 57% having two or more years of experience, though availability of specialized libraries remains a key barrier cited by non-adopters.

Code examples

Basic examples

To illustrate Nim's straightforward syntax for , consider a basic "Hello, World!" program that uses the echo procedure from the to output text to the console.

nim

echo "Hello, World!"

echo "Hello, World!"

This single line compiles to an executable that prints the message when run, highlighting Nim's minimal boilerplate for I/O operations. Nim supports flexible variable declarations, distinguishing between immutable bindings with let and mutable ones with var; types can be explicitly annotated or inferred from initializers.

nim

let message = "immutable string" # Type inferred as string var count: int = 42 # Explicit int type, mutable count = 43 # Reassignment allowed echo message, " has length ", count

let message = "immutable string" # Type inferred as string var count: int = 42 # Explicit int type, mutable count = 43 # Reassignment allowed echo message, " has length ", count

Such declarations promote safe, efficient code by encouraging immutability where possible. A simple recursive procedure for computing the factorial of an integer demonstrates Nim's support for user-defined functions with type annotations and the return statement.

nim

proc [factorial](/page/Factorial)(n: int): int = if n <= 1: return 1 return n * [factorial](/page/Factorial)(n - 1) echo [factorial](/page/Factorial)(5) # Outputs 120

proc [factorial](/page/Factorial)(n: int): int = if n <= 1: return 1 return n * [factorial](/page/Factorial)(n - 1) echo [factorial](/page/Factorial)(5) # Outputs 120

This example shows how Nim handles recursion naturally, with the result inferred for the procedure's return type. For the Fibonacci sequence, an iterative procedure avoids deep recursion while using type inference, loops, and local variables to compute the nth term.

nim

proc fib(n: int): int = var a = 0 var b = 1 if n < 2: return n for i in 2..<n+1: let next = a + b a = b b = next return b echo fib(10) # Outputs 55

proc fib(n: int): int = var a = 0 var b = 1 if n < 2: return n for i in 2..<n+1: let next = a + b a = b b = next return b echo fib(10) # Outputs 55

This approach efficiently calculates sequence values, leveraging Nim's concise loop syntax. String reversal can be implemented manually using a procedure that builds a new string by indexing from the end, demonstrating direct access to string characters without external libraries.

nim

proc reverse(s: string): string = result = newString(s.len) for i in 0..<s.len: result[i] = s[s.len - 1 - i] echo reverse("Nim") # Outputs "miN"

proc reverse(s: string): string = result = newString(s.len) for i in 0..<s.len: result[i] = s[s.len - 1 - i] echo reverse("Nim") # Outputs "miN"

This method preserves the original string's immutability while creating a reversed copy.

Advanced examples

This section presents advanced code examples that integrate multiple Nim features, such as libraries, concurrency primitives, generics, metaprogramming, and foreign function interfaces, to illustrate practical applications in real-world scenarios. These examples build on Nim's multi-paradigm capabilities, combining object-oriented, functional, and procedural elements for more complex tasks.

Graphical user interface

Nim supports cross-platform GUI development through third-party libraries like nimx, which provides a declarative layout system and event handling. The following example creates a simple window with a label using nimx, demonstrating how to set up an application entry point and basic view hierarchy. This integrates Nim's object system with the library's API for rendering a "Hello, world!" message in a resizable window.

nim

import nimx/[window, label, types, layout] app.appName = "Simple Nimx Window" proc main() = var wnd = newWindow(newRect(40, 40, 800, 600)) wnd.makeLayout: - Label: frame == super text: "Hello, world!" textColor: blackColor() fontSize: 24 runApplication: main()

import nimx/[window, label, types, layout] app.appName = "Simple Nimx Window" proc main() = var wnd = newWindow(newRect(40, 40, 800, 600)) wnd.makeLayout: - Label: frame == super text: "Hello, world!" textColor: blackColor() fontSize: 24 runApplication: main()

To compile and run, install nimx via Nimble (nimble install nimx) and use nim c --run yourfile.nim for desktop targets (adjust backend as needed for iOS/Android/WebAssembly). This approach leverages Nim's garbage collection for safe memory handling in UI components.

Concurrent task

Nim's asyncdispatch module enables efficient concurrency for I/O-bound operations, such as HTTP requests, while futures facilitate safe data exchange between concurrent tasks. The example below fetches content asynchronously from a URL using the httpclient library and completes a future with the result to a receiver, combining async/await with non-blocking communication to avoid blocking the main event loop. This pattern is useful for building responsive servers or data pipelines.

nim

import std/[asyncdispatch, httpclient] var ch = newFuture[string]() proc fetchContent(url: string, fut: Future[string]) {.async.} = var client = newAsyncHttpClient() defer: client.close() let content = await client.getContent(url) fut.complete(content) proc main() {.async.} = asyncSpawn fetchContent("http://example.com", ch) let result = await ch echo "Fetched: ", result.len, " bytes" waitFor main()

import std/[asyncdispatch, httpclient] var ch = newFuture[string]() proc fetchContent(url: string, fut: Future[string]) {.async.} = var client = newAsyncHttpClient() defer: client.close() let content = await client.getContent(url) fut.complete(content) proc main() {.async.} = asyncSpawn fetchContent("http://example.com", ch) let result = await ch echo "Fetched: ", result.len, " bytes" waitFor main()

Here, asyncSpawn schedules the fetch without blocking, and the future ensures the content is delivered to the main proc. For production, add error handling with try/except around the await expressions. This integrates functional composition (async procs) with imperative control flow.

Generic data structure

Nim's generics allow type-safe reusable data structures, while pattern matching via the case statement enables destructuring and conditional logic on variants or collections. The following implements a generic stack using a sequence, with push/pop operations and pattern matching to handle empty/non-empty states, demonstrating functional safety checks integrated with object-oriented encapsulation. This example draws from Nim's built-in collection types and control flow for robust implementation.

nim

type Stack*[T] = object data: seq[T] proc initStack*[T](): Stack[T] = result.data = @[] proc push*[T](s: var Stack[T]; item: T) = s.data.add(item) proc pop*[T](s: var Stack[T]): T = case s.data.len of 0: raise newException(IndexError, "Stack is empty") else: result = s.data[^1] s.data.del(s.data.len - 1) # Usage var intStack = initStack[int]() intStack.push(1) intStack.push(2) echo pop(intStack) # Outputs: 2

type Stack*[T] = object data: seq[T] proc initStack*[T](): Stack[T] = result.data = @[] proc push*[T](s: var Stack[T]; item: T) = s.data.add(item) proc pop*[T](s: var Stack[T]): T = case s.data.len of 0: raise newException(IndexError, "Stack is empty") else: result = s.data[^1] s.data.del(s.data.len - 1) # Usage var intStack = initStack[int]() intStack.push(1) intStack.push(2) echo pop(intStack) # Outputs: 2

The pattern matching on s.data.len provides exhaustive checking at compile-time for known ranges, enhancing reliability in generic contexts. For more advanced destructuring, libraries like fusion extend this with object/tuple matching.

Macro usage

Nim's macro system enables powerful metaprogramming for domain-specific languages (DSLs) or code generation, such as automatic logging around expressions. The example defines a simple logging macro that wraps its argument in an echo statement with a timestamp, transforming the AST at compile-time to insert debug output without runtime overhead when disabled. This combines reflection (via quote do) with procedural generation for concise, expressive code.

nim

import std/[macros, times] macro log(body: untyped): untyped = quote do: when declared(debugLog): let ts = now().format("yyyy-MM-dd HH:mm:ss") echo "[" & ts & "] Log: ", `body` else: `body` const debugLog = true # Toggle for production log("Application started") # Expands to echo with timestamp

import std/[macros, times] macro log(body: untyped): untyped = quote do: when declared(debugLog): let ts = now().format("yyyy-MM-dd HH:mm:ss") echo "[" & ts & "] Log: ", `body` else: `body` const debugLog = true # Toggle for production log("Application started") # Expands to echo with timestamp

When debugLog is true, invocations expand to logging calls; otherwise, they inline directly. This macro supports typed arguments for type safety and can be extended to capture caller info using getAst. For DSLs, similar techniques generate boilerplate like serialization code.

FFI call

Nim's foreign function interface (FFI) seamlessly integrates C libraries via pragmas, allowing direct calls to system APIs like stdio for low-level file I/O. The example below imports C's fopen/fclose for reading a file, wrapping pointers in Nim procs for safe usage, and demonstrates how to link against libc without wrappers. This is compiled with --passC:-lc and highlights Nim's C backend for performance-critical interop.

nim

import std/os # For getCurrentDir {.push header: "<stdio.h>".} type FILE = pointer proc fopen(filename, mode: cstring): ptr FILE {.importc.} proc fread(ptr: pointer; size, n: int; file: ptr FILE): int {.importc.} proc fclose(file: ptr FILE): int {.importc.} {.pop.} proc readFileC(filename: string): string = let cFilename = filename.cstring let file = fopen(cFilename, "r".cstring) if file == nil: raise newException(IOError, "Cannot open file") defer: discard fclose(file) let buffer = alloc(1024) # Simple fixed buffer for demo let bytesRead = fread(buffer, 1, 1024, file) result = newString(bytesRead) copyMem(result[0].unsafeAddr, buffer, bytesRead) dealloc(buffer) echo readFileC("example.txt")

import std/os # For getCurrentDir {.push header: "<stdio.h>".} type FILE = pointer proc fopen(filename, mode: cstring): ptr FILE {.importc.} proc fread(ptr: pointer; size, n: int; file: ptr FILE): int {.importc.} proc fclose(file: ptr FILE): int {.importc.} {.pop.} proc readFileC(filename: string): string = let cFilename = filename.cstring let file = fopen(cFilename, "r".cstring) if file == nil: raise newException(IOError, "Cannot open file") defer: discard fclose(file) let buffer = alloc(1024) # Simple fixed buffer for demo let bytesRead = fread(buffer, 1, 1024, file) result = newString(bytesRead) copyMem(result[0].unsafeAddr, buffer, bytesRead) dealloc(buffer) echo readFileC("example.txt")

This FFI call bypasses Nim's stdlib for direct access, useful for legacy integration or optimization. Error checking via nil pointers and defer ensures resource cleanup, aligning with Nim's RAII-like semantics. For complex libraries, tools like c2nim automate bindings.

Community and adoption

Online communities

The programming language maintains an active online presence through several dedicated platforms that facilitate discussion, learning, and collaboration among developers. The official Nim forum at forum.nim-lang.org serves as a primary hub for question-and-answer sessions, project sharing, and official announcements from the language maintainers. This forum, powered by NimForum software developed within the ecosystem, hosts threads on language features, troubleshooting, and community-driven initiatives. For more casual interactions, the r/nim subreddit provides a space for news updates, beginner questions, and informal discussions about applications in various domains. With thousands of subscribers, it often features links to recent releases, user projects, and debates on best practices. The organization at nim-lang hosts the core repositories for the and , enabling users to report issues, submit pull requests, and contribute to development. This platform is central to the open-source workflow, with active issue trackers and contribution guidelines that support both novice and experienced developers. Real-time communication occurs via the #nim channel on IRC at irc. and the official server, which bridges with IRC for seamless interaction. These channels are used for quick queries, sessions, and immediate feedback on code snippets. The official documentation site at nim-lang.org offers comprehensive tutorials, manuals, and resources for learning Nim, while the associated blog publishes updates on language evolution, case studies, and ecosystem news. According to the 2024 Stack Overflow Developer Survey, approximately 1% of over 60,000 respondents reported using , with 41.9% of those users expressing a desire to continue working with it; the community primarily attracts developers focused on systems and web programming.

Events and contributions

The Nim community organizes annual events to foster collaboration and knowledge sharing, including the online NimConf conference, which took place on October 26, 2024, featuring talks on language features and applications. Additionally, Nim has a presence at larger open-source gatherings, such as dedicated devrooms and workshops at ; for instance, the 2022 FOSDEM Nim devroom covered topics like concurrency and microcontroller programming, while 2025 sessions included presentations on Nim's interoperability with Python and its C integration for performance-critical tasks. Contributions to Nim are facilitated through its repository, where developers submit pull requests for code changes, which undergo review and require passing tests before merging. Bug reports and feature requests are also encouraged via the issue tracker, and dedicated individuals can join core team roles by demonstrating consistent involvement in the project. The governance model operates as a led by creator Rumpf, who makes final decisions while incorporating community input through discussions and reviews. Nim's adoption spans diverse applications, including Discord bots built with libraries like dimscord, which provides API integration for bot development. In gaming, bindings such as naylib enable 2D and 3D game creation using the library, supporting cross-platform titles including mobile games. For embedded systems and IoT, Nim compiles to C, allowing deployment on microcontrollers and resource-constrained devices, as demonstrated in projects targeting platforms like . Recent trends from to highlight a dedicated but modest user base, with the 2024 community survey receiving 367 responses—indicating steady interest despite fewer participants than prior years—and showing 28% of respondents using Nim for over 60% of their programming work, an increase from 20% the previous year. Embedded development has emerged as a leading use case, surpassing targets in popularity. There's growing emphasis on web3 applications, exemplified by the Nimbus Ethereum clients implemented in Nim for efficient blockchain node operation. Community priorities for 2025 include enhancing tooling, resolving compiler issues, and expanding documentation to support broader adoption.

References

Add your contribution
Related Hubs
User Avatar
No comments yet.