What makes Nim practical?

In my last post I showed what makes the Nim programming language special. Today, let’s consider Nim from another angle: What makes Nim a practical programming language?

Binary Distribution

Programs written in interpreted languages like Python are difficult to distribute. Either you require Python (in a specific version even) to be installed already, or you ship it with your program. This even causes some to reconsider Python as a teaching language.

How does Nim work around this problem? For starters your program gets statically linked against the Nim runtime. That means you end up with a single binary that depends solely on the standard C library, which we can take for granted on any operating system we’re interested in.

Let’s write a small program and give this a try:

echo"Hello World"

If you want to follow along, get the Nim compiler. Save this code as hello.nim. Let’s compile it now so that we can distribute the hello binary:

Now our little program starts growing and we get interested in using a few Nim libraries. Do we have to add compiled versions of these libraries to our distributions now? Do not fret! Nim libraries are statically compiled into our binary as well. Let’s get a library using Nim’s package manager, nimble:

The only dynamic libraries we have to worry about are the C libraries we use, but that’s a story for another time.

Source Distribution

We can’t compile our program for all possible combinations of operating systems and CPU architectures of course. But we can be prepared for them and create a source distribution that can be compiled on the target systems with just a C compiler:

We want our program to never access out of sequence bounds, so we want to compile with nim -d:release --checks:on max. As it’s unreasonable to write this every time, we can create a max.nim.cfg file in our directory instead and enable runtime checks for every compilation of our program:

checks: on

Now we run into the problem that the max function runs too slowly (it doesn’t, but let’s imagine). So we disable runtime checks just for this function:

Great, now we can control which part of our program has runtime checks and which doesn’t. checks enables and disables all runtime checks at once, but there are more fine-grained controls as well.

Nim instead of Python + C++

High performance languages like C++ may require some boilerplate. A higher level language can be used at compile time to automatically create the boilerplate. In DDNet (which inherited them from Teeworlds) there are many examples for this.

A really simple use case is to get the current git revision at compile time in Python and put it into the config.h with a #define so it can be referred to at runtime in the program. In Nim you need neither Python nor a C preprocessor and can instead do it all directly at compile time in Nim:

constgitHash=staticExec"git rev-parse HEAD"echo"Revision: ",gitHash

Use as a scripting language

With TinyCC as the backend Nim makes for a nice scripting language. All we need is a nimscript file and the TinyCC compiler:

Wrapping libraries with c2nim

Nice C libraries can automatically be converted into a Nim wrapper with c2nim. Let’s do this for Bellard’s new BPG image format. Since the library is so new, there is no shared library compilation included yet, so I added that myself in my fork.

We need to fix up the C header for c2nim by adding this at the top of libbpg.h:

Note that you can tell from the discard statements where I chose to ignore errors. If you actually want to run this, check out the instructions in the repository.

Final words

That’s Nim from a more practical angle. Hopefully you’ll consider Nim for your next project, there are many libraries available already. Also, the community always needs more helping hands!

To do my part in making Nim more practical, I’m trying to implement a new, working REPL using TinyCC. We may also soon get a working compiler as a service for proper IDE integration, directly from Andreas Rumpf.