Version 1.6.0 released

Nim version 1.6 is now officially released!

A year in the making, 1.6 is the latest stable release and by far the largest yet. We’re proud of what we — the core team and dedicated volunteers — have accomplished with this milestone:

  • 1667 PRs merged (1760 commits)
  • 893 issues closed
  • 15 new stdlib modules
  • new features in more than 40 stdlib modules, including major improvements to 10 commonly used modules
  • documentation and minor improvements to 170 modules, including 312 new runnable examples
  • 280 new nimble packages

Nim made its first entry in TIOBE index in 2017 at position 129, last year it entered the top-100, and for 2 months the top-50 (link). We hope this release will reinforce this trend, building on Nim’s core strengths: a practical, compiled systems programming language offering C++-like performance and portability, Python-like syntax, Lisp-like flexibility, strong C, C++, JS, Python interop, and best-in-class metaprogramming.

This release includes improvements in the following areas:

  • new language features (iterable[T], user-defined literals, private imports, strict effects, dot-like operators, block arguments with optional parameters)
  • new compiler features (nim --eval:cmd, custom nimscript extensions, customizable compiler messages)
  • major improvements to --gc:arc and --gc:orc
  • correctness and performance of integer and float parsing and rendering in all backends
  • significant improvements in error messages, showing useful context
  • documentation generation logic and documentation, in particular runnableExamples now works in more contexts
  • JS, VM and nimscript backend are more consistent with the C backend, allowing more modules to work with those backends, including the imports from std/prelude; the test suite now standardizes on testing stdlib modules on each major backend (C, JS, VM)
  • support for Apple silicon/M1, 32-bit RISC-V, armv8l, CROSSOS, improved support for NodeJS backend
  • major improvements to the following modules: system, math, random, json, jsonutils, os, typetraits, wrapnils, lists, hashes including performance improvements
  • deprecated a number of error prone or redundant features

Why use Nim?

Last but not least, macros let you manipulate/generate code at compile time instead of relying on code generators, enabling writing DSLs and language extensions in user code. Typical examples include implementing Python-like f-strings, optional chaining, command line generators, React-like Single Page Apps, protobuf serialization and binding generators.

Installing Nim 1.6

We recommend everyone to upgrade to 1.6:

New users

Check out if your package manager already ships version 1.6 or install it as described here.

Note: earlier this year researchers spotted malware written in Nim programming language which supposedly led to antivirus vendors falsely tagging all software written in Nim as a potential threat, including the Nim compiler, nimble (Nim’s package manager) and so on (core Nim tooling is written entirely in Nim). This has been an ongoing issue ever since - if you have any issues related to this, please report the Nim compiler and associated tooling as false detection to the respective antivirus vendors.

Existing users

If you have installed a previous version of Nim using choosenim, getting Nim 1.6 is as easy as:

choosenim update self
choosenim update stable

If you don’t have choosenim, you can follow the same install link as above.

Building from source

git clone
cd Nim

The last command can be re-run after pulling new commits. Note that the csources repo used was changed to csources_v1, the new setup is designed to be forward and backward compatible.

Building from a CI setup

We now have bash APIs to (re-)build Nim from source which hide implementation details, for example: . ci/ && nimBuildCsourcesIfNeeded. This can be useful for CI when alternatives (using nightly builds or a Docker image) are not suitable; in fact all the existing CI pipelines have been refactored to use this, see #17815.

Contributors to Nim 1.6

Many thanks to our recurring and new contributors. Nim is a community driven collaborative effort that welcomes all contributions, big or small.

Backward compatibility and preview flags

Starting with this release, we’ve introduced preview flags of the form -d:nimPreviewX (e.g. -d:nimPreviewFloatRoundtrip), which allow users to opt-in to new stdlib/compiler behavior that will likely become the default in the next or a future release. These staging flags aim to minimize backward compatibility issues.

We also introduced opt-out flags of the form -d:nimLegacyX, e.g. -d:nimLegacyCopyFile, for cases where the default was changed to the new behavior. For a transition period, these flags can be used to get the old behavior.

Here’s the list of these flags introduced in this release, refer to the text below for explanations:

  • -d:nimLegacyCopyFile
  • -d:nimLegacyJsRound
  • -d:nimLegacyMacrosCollapseSymChoice
  • -d:nimLegacyParseQueryStrict
  • -d:nimLegacyRandomInitRand
  • -d:nimLegacyReprWithNewline
  • -d:nimLegacySigpipeHandler
  • -d:nimLegacyTypeMismatch
  • -d:nimPreviewDotLikeOps
  • -d:nimPreviewFloatRoundtrip
  • -d:nimPreviewHashRef
  • -d:nimPreviewJsonutilsHoleyEnum

Major new features

With so many new features, pinpointing the most salient ones is a subjective exercise, but here are a select few:


The iterable[T] type class was added to match called iterators, which solves a number of long-standing issues related to iterators. Example:

iterator iota(n: int): int =
  for i in 0..<n: yield i

# previously, you'd need `untyped`, which caused other problems such as lack
# of type inference, overloading issues, and MCS.
template sumOld(a: untyped): untyped = # no type inference possible
  var result: typeof(block:(for ai in a: ai))
  for ai in a: result += ai

assert sumOld(iota(3)) == 0 + 1 + 2

# now, you can write:
template sum[T](a: iterable[T]): T =
  # `template sum(a: iterable): auto =` would also be possible
  var result: T
  for ai in a: result += ai

assert sum(iota(3)) == 0 + 1 + 2 # or `iota(3).sum`

In particular iterable arguments can now be used with the method call syntax. For example:

import std/[sequtils, os]
echo walkFiles("*").toSeq # now works

See PR #17196 for additional details.

Strict effects

The effect system was refined and there is a new .effectsOf annotation that does explicitly what was previously done implicitly. See the manual for more details. To write code that is portable with older Nim versions, use this idiom:

when defined(nimHasEffectsOf):
  {.experimental: "strictEffects".}
  {.pragma: effectsOf.}

proc mysort(s: seq; cmp: proc(a, b: T): int) {.effectsOf: cmp.}

To enable the new effect system, compile with --experimental:strictEffects. See also #18777 and RFC #408.

Private imports and private field access

A new import syntax import foo {.all.} now allows importing all symbols (public or private) from foo. This can be useful for testing purposes or for more flexibility in project organization.


from system {.all.} as system2 import nil
echo system2.ThisIsSystem # ThisIsSystem is private in `system`
import os {.all.} # weirdTarget is private in `os`
echo weirdTarget # or `os.weirdTarget`

Added a new module std/importutils, and an API privateAccess, which allows access to private fields for an object type in the current scope.


import times
from std/importutils import privateAccess
  let t = now()
  # echo t.monthdayZero # Error: undeclared field: 'monthdayZero' for type times.DateTime
  privateAccess(typeof(t)) # enables private access in this scope
  echo t.monthdayZero # ok

See PR #17706 for additional details.

nim --eval:cmd

Added nim --eval:cmd to evaluate a command directly, e.g.: nim --eval:"echo 1". It defaults to e (nimscript) but can also work with other commands, e.g.:

find . | nim r --eval:'import strutils; for a in stdin.lines: echo a.toUpper'
# use as a calculator:
nim --eval:'echo 3.1 / (1.2+7)'
# explore a module's APIs, including private symbols:
nim --eval:'import os {.all.}; echo weirdTarget'
# use a custom backend:
nim r -b:js --eval:"import std/jsbigints; echo 2'big ** 64'big"

See PR #15687 for more details.

Round-trip float to string

system.addFloat and system.$ now can produce string representations of floating point numbers that are minimal in size and possess round-trip and correct rounding guarantees (via the Dragonbox algorithm). This currently has to be enabled via -d:nimPreviewFloatRoundtrip. It is expected that this behavior becomes the new default in upcoming versions, as with other nimPreviewX define flags.


from math import round
let a = round(9.779999999999999, 2)
assert a == 9.78
echo a # with `-d:nimPreviewFloatRoundtrip`: 9.78, like in python3 (instead of  9.779999999999999)

New std/jsbigints module

Provides arbitrary precision integers for the JS target. See PR #16409. Example:

import std/jsbigints
assert 2'big ** 65'big == 36893488147419103232'big
echo 0xdeadbeef'big shl 4'big # 59774856944n

New std/sysrand module

Cryptographically secure pseudorandom number generator, allows generating random numbers from a secure source provided by the operating system. Example:

import std/sysrand
assert urandom(1234) != urandom(1234) # unlikely to fail in practice

See PR #16459.

New module: std/tempfiles

Allows creating temporary files and directories, see PR #17361 and followups.

import std/tempfiles
let tmpPath = genTempPath("prefix", "suffix.log", "/tmp/")
# tmpPath looks like: /tmp/prefixpmW1P2KLsuffix.log

let dir = createTempDir("tmpprefix_", "_end")
# created dir looks like: getTempDir() / "tmpprefix_YEl9VuVj_end"

let (cfile, path) = createTempFile("tmpprefix_", "_end.tmp")
# path looks like: getTempDir() / "tmpprefix_FDCIRZA0_end.tmp"
cfile.write "foo"
cfile.setFilePos 0
assert readAll(cfile) == "foo"
close cfile
assert readFile(path) == "foo"

User-defined literals

Custom numeric literals (e.g. -128'bignum) are now supported. Additionally, the unary minus in -1 is now part of the integer literal, i.e. it is now parsed as a single token. This implies that edge cases like -128'i8 finally work correctly. Example:

func `'big`*(num: cstring): JsBigInt {.importjs: "BigInt(#)".}
assert 0xffffffffffffffff'big == (1'big shl 64'big) - 1'big

Dot-like operators

With -d:nimPreviewDotLikeOps, dot-like operators (operators starting with ., but not with ..) now have the same precedence as ., so that a.?b.c is now parsed as (a.?b).c instead of a.?(b.c). A warning is generated when a dot-like operator is used without -d:nimPreviewDotLikeOps.

An important use case is to enable dynamic fields without affecting the built-in . operator, e.g. for std/jsffi, std/json, pkg/nimpy. Example:

import std/json
template `.?`(a: JsonNode, b: untyped{ident}): JsonNode =
let j = %*{"a1": {"a2": 10}}
assert j.?a1.?a2.getInt == 10

Block arguments now support optional parameters

This solves a major pain point for routines accepting block parameters, see PR #18631 for details:

template fn(a = 1, b = 2, body) = discard
fn(1, 2): # already works
fn(a = 1): # now works

Likewise with multiple block arguments via do:

template fn(a = 1, b = 2, body1, body2) = discard
fn(a = 1): # now works

Other features

For full changelog, see here.


Tested on a 2.3 GHz 8-Core Intel Core i9, 2019 macOS 11.5 with 64GB RAM.

  • [1] command used: nim c -d:danger. The binary size can be further reduced to 49K with stripping (--passL:-s) and link-time optimization (--passC:-flto). Statically linking against musl brings it under 5K - see here for more details.
  • [2] commands used:
    • for Nim: nim c --forceBuild compiler/nim
    • for Rust: ./ build, details
    • for GCC: see 1 2
    • for Clang: details
    • for Go: ./make.bash
  • [3] a separate nimscript file can be used if needed to execute code at compile time before compiling the main program but it’s in the same language