What is special about Nim?

The Nim programming language is exciting. While the official tutorial is great, it slowly introduces you to the language. Instead I want to quickly show what you can do with Nim that would be more difficult or impossible in other languages.

I discovered Nim in my quest to find the right tools to write a game, HookRace, the successor of my current DDNet game/mod of Teeworlds. Since I’m busy with other projects for now, this blog is now officially about Nim instead, until I find time to continue developing the game.

Easy to get running

Ok, this part is not exciting yet, but I invite you to follow along with the post:

foriin0..10:echo"Hello World"[0..i]

If you want to do so, get the Nim compiler. Save this code as hello.nim, compile it with nim c hello and finally run the binary with ./hello. To immediately compile and run, use nim -r c hello. To use an optimized release build instead of a debug build use nim -d:release c hello. With all of these settings you will see the following output:

Run regular code at compile time

To implement an efficient CRC32 procedure you need a lookup table. You could compute it at runtime or write it into your code as a magic array. Clearly we don’t want any magic numbers in our code, so we’ll do it at runtime (for now):

Great, this works and we get 414FA339 as the output. But it would be even better if we could compute the CRC table at compile time. This is as easy as it gets in Nim, instead of the current crc32table creation we use:

# Table created at compile timeconstcrc32table=createCRCTable()

Yes, that’s right: All we had to do was replace the var with a const. Beautiful, isn’t it? We can write the exact same code and have it run at runtime and compile time. No template metaprogramming necessary.

Extend the language

Templates and macros can be used to get rid of boilerplate, by transforming your code at compile time.

Templates just replace their invocations with their code at compile-time. We can define our own loops like this:

If you’re curious about the 10.times: syntax, it’s just a regular call to times with 10 as the first parameter and the following indented block as the second parameter. Instead you could also write times(10):, see Unified Call Syntax.

Macros go a step further and allow you to analyze and manipulate the AST. There are no list comprehensions in Nim, for example, but it’s possible to add them to the language by using a macro. Now instead of this:

In the first term rewriting template we specify that a * 2 can be replaced by a + a. In the second one we specify that ints in a multiplication can be swapped if the first is an integer literal, so that we can potentially apply the first template.

More complicated patterns can also be implemented, for example to optimize boolean logic:

Control the garbage collector

To achieve soft realtime, you can tell the garbage collector when and for how long it is allowed to run. The main game logic can be implemented like this in Nim, to prevent the garbage collector from causing stutters:

You can’t accidentally add a value of another type. Internally the set works as an efficient bitvector.

The same is possible with arrays, indexing them with your enum:

vara:array[FakeTune,int]a[freeze]=100echoa[freeze]

Unified Call Syntax

This is just syntactic sugar, but it’s definitely nice to have. In Python I always forget whether len and append are functions or methods. In Nim I don’t have to remember, because there is no difference. Nim uses Unified Call Syntax, which has now also been proposed for C++ by Herb Sutter and Bjarne Stroustrup.

Good performance

I made some measurements on my machine when the benchmark was first published (Linux x86-64, Intel Core2Quad Q9300 @2.5GHz, state of 2014-12-20):

Lang

Time [ms]

Memory [KB]

Compile Time [ms]

Compressed Code [B]

Nim

1400

1460

893

486

C++

1478

2717

774

728

D

1518

2388

1614

669

Rust

1623

2632

6735

934

Java

1874

24428

812

778

OCaml

2384

4496

125

782

Go

3116

1664

596

618

Haskell

3329

5268

3002

1091

LuaJit

3857

2368

-

519

Lisp

8219

15876

1043

1007

Racket

8503

130284

24793

741

Compressed code size with gzip -9 < nim.nim | wc -c. Removed unused functions in Haskell. Compile times are clean compiles, if you have a nimcache with the standard library precompiled it’s only 323 ms for Nim.

Another small benchmark I did, calculating which numbers of the first 100 million are prime, in Python, Nim and C:

Compile to JavaScript

You can compile Nim to JavaScript, instead of C. This allows you to write the client as well as the server directly in Nim. Let’s make a small visitor counter on the server that gets displayed in the browser. This is our client.nim:

We define a Data type that we use to pass data from the server to client. The printInfo procedure will be called with this data and display it. Compile this with nim js client. The result javascript file ends up in nimcache/client.js.

The server delivers the main website. It also delivers the client.js, by compiling and reading the client.nim at compile time. The logic is in the /visitors handling. Compile and run with nim -r c server and open http://localhost:5000/ to see the code in effect.

Final words

I hope I could pique your interest in in the Nim programming language.

Note that the language is not completely stable yet. Especially with the more obscure features you may run into bugs. But on the bright side, Nim 1.0 is supposed to be released within the next 3 months! So now is the perfect time to get started with learning Nim.

Bonus: Since Nim compiles to C and only depends on the C standard library you can deploy it nearly everywhere, including x86-64, ARM and Intel Xeon Phi accelerator cards.