Nim impressions

Several months ago I finally got around to checking out Nim, a programming language that has otherwise been on the periphery of my awareness for a while.

What is Nim? The Nim website says this:

Nim is a statically typed compiled systems programming language. It combines successful concepts from mature languages like Python, Ada and Modula.

Nim looks kind of like Python, is statically typed, employs a (tweakable) garbage collector, and compiles to C. Its intended niche is probably similar to that of languages like Go.

The first release of Nim happened in 2008, and 1.0—the mature release version of Nim—came out in 2019. The language remains somewhat obscure, and as a result there is not a lot of popular software written in Nim, even if the language does retain a dedicated community of users.

Diving in

Nim (like many of its contemporaries) has a version manager, called choosenim. Installing Nim can therefore involve piping curl to sh, and having the tool download the compiler and adjacent tooling to your home directory. Of course, for those who prefer to avoid such controversial methods of installing language tooling, there are also more traditional packages around, as well as the option of building Nim yourself (which requires a C compiler).

The Nim website offers some introductory tutorials and a fairly readable manual to get you started with the language. A Github wiki contains some pointers on getting started for both Python and C programmers, which can be a useful resource for "how do I do this common thing in Nim" type of questions.

Parts of the tutorial refer you to the language manual, which is in places more up to date than the tutorial. The manual, which is a draft and "evolving into a proper specification", is fortunately not overly dense, and does a pretty good job of describing how the language is used.

Overall, the language itself does not introduce any extremely novel concepts, and is fairly easy to pick up without reading extensive introductory documentation.

Case insensitivities

When getting into Nim, one is likely to quickly notice some of the more unusual choices in the language's design. Whether they are good or bad is debatable, but they are unusual.

One example of a Nim oddity is partial case insensitivity. Nim is a language in which identifiers are compared in a mostly case-insensitive manner. Underscores in identifiers are also ignored for purposes of comparison, and as a result im_an_identifier and imAnIdentifier are functionally the same. Note that the comparisons are mostly case-insensitive—comparisons of the first character are not case-insensitive, so that you can signal if something is an class or a procedure by the starting letter: Foo vs fooize().

Is this a good decision? I'm inclined to say no, and a lot of people are probably inclined to say no as well. One can, however, try to see it from the perspective of the designers: case insensitivity means you can use whatever spelling you prefer in your own code, even if using foreign libraries. In practice, however, most programmers probably do not consider this a problem that needed a solution. The practical result of this for me was that I would accidentally mistype capitalizations, resulting in stuff like OBject, which did not bother the compiler, but is perhaps a bit of an ugly thing to have in your code. I suppose someone could always write a linter, though.

Pythonness

When it comes to syntax, Nim seems to draw heavy inspiration from Python.

We have to consider, however, that Python's indentation-sensitive design tends to be informed by core Python principles, and tends to guide Python code towards being clean and readable. Nim, on the other hand, both incorporates some perhaps less Pythonic features, while also being statically typed and having scope rules different than Python. The result is something that resembles Python visually, but does not necessarily resemble Python in how Python is written.

Unlike Python, Nim tends to allow more than one way to do anything, the case insensitivity being a prominent example of that. It also tends to incorporate features from the curly brace kind of languages, adapting them to the indented style. The result is somewhere between Python and a modern statically typed language.

Consider, for example, this simple program, which prints "Hello!".

let
  enthusiastic = true
  greeting = if enthusiastic:
    "Hello!"
  else:
    "Hello."

echo greeting
A Nim program that prints "Hello!"

The example includes a let block, which can be used instead of let before every assignment, an ifelse expression which returns a value, and a function call without parentheses (which are optional). You probably would not see Python looking like that, even if it had some of these features in some other form.

Cness

Nim, by default, compiles to C, and there are some things that remind the programmer of that. For example, in Nim source files, the order of function definitions matters. While there are no header files, functions do have to be defined, or at least declared, before they are used. There is an experimental code reordering feature, but it is experimental, and has to be explicitly enabled.

Other aspects of the language feel C-like, even if they are not explicitly C (because C does not actually have many of the features that Nim has). For example, Nim imports are unqualified by default. It also has no class methods, but uses Uniform Calling Syntax—foo.bar() is equivalent to foo(bar) for any bar that is of the correct type. These decisions impact the readability and ergonomics of the language, even if in the language's designers' opinions the impact is acceptable.

The standard library

The Nim standard library aims to cover the basics, and it covers some of the basics. There are, for example, useful functions for handling strings, byte streams, or writing unit tests. As useful as it is, however, sometimes it feels like it is not as comprehensive as it could be.

For example, there is a parseopt module, which supplies a command line option parser. The module works, but is somewhat limited in its flexibility, and cannot always imitate the usual getopt conventions. For example, -j4 works, but -j 4 will not—you need to use -j=4, or (and this is a more unusual Nim thing) -j:4.

Of course, there are third party libraries that fill in the functionality here, and parseopt is rather minimal, so the standard library isn't a sprawling behemoth, but this sort of a pattern seems to repeat throughout: the standard library is somewhere between limited and bare bones and batteries included.

Features

I have not been following Nim's development over the years, but just from looking at it now, some of its features feel rather tacked on.

For example, Nim has iterators, which resemble what that other in other languages would be called generators (see example). The default kind of iterator in Nim is inline—it looks like a function, but uses yield statements, and the compiler inlines the for loop block at the yields, in essence making the iterator somewhat like a fancy macro. Since there are some limits inherent in the inline iterator approach, there are also first class iterators available in Nim, indicated with the pragma {.closure.} (Nim pragmas are interesting-looking).

iterator fizzbuzz(count: int): string {.closure.} =
  for number in countup(1, count):
    if (number mod 15) == 0:
      yield "fizzbuzz"
    elif (number mod 3) == 0:
      yield "fizz"
    elif (number mod 5) == 0:
      yield "buzz"
    else:
      yield $number

for element in fizzbuzz(15):
  echo element
An example of a closure iterator

Nim having two different types of iterators is a solution that works, but it feels rough. In some places, the two kinds of iterators behave the same, but in others they do not. First-class iterators can do many of the things that iterators in other programming languages can do, but there are gaps, and some things require more complex solutions to accomplish.

This is, of course, a problem that arises with any evolving programming language, and the design of Nim makes some effort to address such problems, but despite having passed version 1.0, the language can sometimes feel unfinished in this way.

Community

While I haven't personally interacted with the broad Nim community, I have seen the results of other people interacting with it, and, to put it mildly, the Nim community could be more welcoming and inclusive. People have described disagreements which escalated to one of the parties being addressed with bigoted language and harassed even beyond official communication channels of the project.

These days, modern languages in positions similar to that of Nim will generally adopt some form of a code of conduct (often after and despite much vitriol directed towards the idea), and—at least notionally—subscribe to the idea that certain behaviors have the effect of making people unwelcome in the community, and thus should be avoided. Nim maintainers have apparently opted to not take that approach.

Of course, you don't need to interact with the official communication channels of a language to use it, and bigotry found within it may not affect you personally, but these things do signal to some people that they are not welcome. A community which is abrasive will discourage participation from new people. A community which is okay with bigotry will further discourage participation by anyone who is a member of the groups the bigotry is directed against. A language whose main community pushes people away is a language more people will stay away from.

Overall impressions

Nim feels like a grab bag of interesting programming language ideas behind a relatively readable syntax. While there are lot of things to nitpick about the language, it is also doesn't introduce too many esoteric concepts and so is relatively easy to get pick up and start writing stuff in.

Does the language fill any niche that other languages do not? Probably not. Are those other languages more popular, and with a less toxic community? Probably yes. Nim is not a bad language, and it is an interesting language, but it is probably not sufficiently compelling to make you go out of your way to use it.

My thing

If you are interested in looking at something I wrote with no prior experience with Nim, you can check out nfortune, which is a rewrite of fortune-mod, the program that prints random quotes.