A Python Substitute? I Tried Out the Best Programming Language You’ve Never Heard Of

Meet Nim: the language with Python-like syntax and C efficiency

Yakko Majuri
Jul 17 · 10 min read
Image for post
Image for post
While quite representative, the image above is for illustrative purposes only, i.e., it isn’t an actual chart of real results. (Image source: Author)

A few weeks ago I was browsing through GitHub and encountered a repo that caught my eye. It hosted a project that was written entirely in Nim.

What the hell is Nim? I thought.

Immediately I concluded this was one of the many programming languages that a lot of people use, but I just was the stupid guy who didn’t know it. But rather than putting it aside, I decided: Let’s learn a bit about this.

I then had two major realizations:

  • No, this language is not used by a lot of people.
  • But maybe it should be.

So here goes a bit about my experience with Nim, a quick programming tutorial, and why the language seems extremely promising to me.

Show Me the Code!

Here’s a useless program I wrote in Nim:

Image for post
Image for post

Looks pretty clean. It also feels so simple that you can probably figure out what it does with no effort, even though you might have never heard of Nim before. (Hint: It prints num: 5 i: 5.)

So let’s break down what seems familiar here:

Variable declarations

It’s extremely familiar to JavaScript developers. While some languages use var and some use let, JS and Nim both allow declarations using either of the two. It’s important to note that they do not have the same meaning in both languages, however. But more on that later.

Block syntax

To mark a new block in Nim, we use a colon followed by an indented line. That’s Python right there.

Keywords

Both loops, as well as the if statement, look like they were plucked right out of Python. In fact, everything from line 5 onwards is actually valid Python (assuming we had an echo function defined).

So yes, a lot of the keywords and operators from Python are also valid in Nim, such as not , is , and , or , etc.

Until now, there’s nothing special about Nim at all. It just looks like a worse version of Python (syntax-wise), since we need to use let or var.

But what if I told you this: Nim is a statically typed language that runs almost as fast as C.

Oh, now we’re talking.

A Friendly Race

Image for post
Image for post
Photo by Victoire Joncheray

Before diving a little deeper into the Nim syntax (especially the statically typed part, which we still haven’t seen), let’s try to back up the claims about its speed. To do so, I wrote a program to naively (i.e., without dynamic programming) compute the n-th Fibonacci number in Nim, Python, and C.

In order to keep things fair, I standardized the implementation based on the suggested Leetcode solution for this problem (Approach 1) and made sure to stick to it as much as possible across the three languages.

EDIT: People keep telling me about LRU Cache. I am aware of it. However, the implementations in all the languages could be improved with memoization. The point here is just to pick a standard implementation and use it, not try to optimize as much as possible. Hence I picked a naive implementation.

To time the execution, I used the “real” value from the output of time in the Bash shell.

Here are the results for computing the 40th Fibonacci number:

Image for post
Image for post
Average speed of ten runs, with the 1st “cold” run being discarded (see this for why).

Yeah, that happened.

Now, this is very limited and far from a scientific experiment, but it is consistent with the findings of others who have done more serious benchmarks [1][2][3].

Nevertheless, all the code I’ll run through in this article is available on GitHub, including instructions on how to try this experiment out.

Edit: It has come to my attention that by simply passing a flag to the Nim compiler I could have improved its speed of execution by over 10x. Some have done the tests and found Nim to be even faster than C. This happens because, as stated in the Official Docs: “By default the Nim compiler generates a large amount of runtime checks aiming for your debugging pleasure. With -d:releasesome checks are turned off and optimizations are turned on.”

The graph will not be updated for the sake of finality, but I thought this note was important. Refer to this issue for more information.

So why is Nim so much faster than Python?

Well, I would say there are two main reasons. Simplifying heavily, these are:

  • Nim is compiled while Python is interpreted (I know it’s a debate, but I’m pulling this from the Docs to keep it short). This means that when a Python program is run, there’s more work being done than just running the program, as it needs to be interpreted before it can actually execute. This generally makes languages much slower.
  • Nim is statically typed. While the example I showed earlier didn’t have a single type declaration or annotation, we will see later that it is indeed a statically typed language. In the case of Python, which is dynamically typed, the interpreter needs to do a lot more work to figure out and appropriately handle types, which slows down execution.

Faster to Run, Slower to Write

Here’s what the Python Docs have to say about interpreted languages:

“Interpreted languages typically have a shorter development/debug cycle than compiled ones, though their programs generally also run more slowly.”

This sentence is a good summary of the tradeoff between Python and C, for example. Anything you can do with Python you can also do with C, and your program will run many orders of magnitude faster.

However, you will be spending a lot more time writing and debugging your code in C, as well as it will be longer and less readable. And that’s why C isn’t as trendy anymore and Python is so popular. To put it simply: Python is “easy” (comparatively, of course).

So, if Python is on one end of the spectrum and C is on the other, Nim is trying to be somewhere in the middle: somewhat fast and somewhat easy? Something like that.

What makes Nim stand out for me, however, is that at first glance it feels like it has minimized the tradeoff. In other words, it is much faster than Python, but not equally harder to program in, like C (or so it feels like upon brief inspection).

To illustrate this point, let’s look at the code from our Fibonacci experiment.

Here’s the C code:

Python:

And Nim:

While Nim has this weird proc thing and, dear God, uses = to declare functions (or procedures, as they’re called), it’s still much cleaner than C.

Hence, maybe it’s a worthy tradeoff? A little harder to write than Python, but tens of times faster — I could live with that.

Nim Syntax

Here’s a brief overview of some key points about Nim’s syntax:

Variables

Variables are declared with var , let , or const .

var and const work essentially like JavaScript, but let is a different story.

While let in JavaScript differs from var in terms of scope, let in Nim denotes a variable whose value cannot change after initialization. This is apparently similar to Swift, I’ve now been told.

But isn’t that a constant?

Well, in Nim the distinction is as follows:

For const, the compiler must be able to determine the value at compile time, whereas let can be determined at runtime.

The documentation offers this example:

const input = readLine(stdin) # Error: constant expression expectedlet input = readLine(stdin)   # works

Additionally, you can also declare variables like this:

var
a = 1
b = 2
c = 3
x, y = 10 # Both x, y are assigned to 10

Functions

Functions in Nim are called procedures, and their declaration is done like this:

proc procedureName(parameterName: parameterType):returnType =
return returnVar

Given that the language looks like Python in a lot of ways, procedures definitely stand out as being a little weird when you first see them.

Using = instead of { or : is especially odd. However, it looks a little better in one-liner procedures:

proc hello(s: string) = echo s

Also, you’re able to return from functions like this:

proc toString(x: int): string = 
result =
if x < 0: “negative”
elif x > 0: “positive”
else: “zero”

It feels like you should still do return result, but result is not a variable — it’s a keyword. The above snippet is perfectly valid Nim.

And you’re also able to overload procedures:

Control flow

It’s a lot like Python.

# if true:# while true:# for num in nums:

For iterating over a range, instead of a range you can use countup(start, finish), or countdown(start, finish) . Or you can simplify the whole thing and use:for i in start..finish.

Printing and getting user input

let input = readLine(stdin)
echo input

When comparing to Python, readLine(stdin) is the equivalent of input() and echo is the equivalent of print.

echo can be used with or without parentheses.

My goal here is to give you a taste of Nim, not go through their entire manual. As such, I think I’ll stop here for plain syntax and skim over some additional features next.

Additional Features

Object-oriented programming

Nim is not object-oriented but has minimalistic support for objects. They are not as neat as Python classes, however.

Macros

Nim supports macros and metaprogramming, and, in fact, seems to heavily emphasize it. An entire part of their three-part tutorial series is dedicated to it.

Here’s a quick example:

import macros  macro myMacro(arg: static[int]): untyped =   
echo arg

myMacro(1 + 2 * 3)

Basic types

The basic types in Nim are: string, char,bool, int, uint, and float.

These are also valid:

int8 int16 int32 int64 uint8 uint16 uint32 uint64 float32 float64

Additionally, strings are mutable in Nim, unlike in Python.

Comments

You probably already saw my comments in Python syntax above, but unlike Python, multiline comments also make use of the hash symbol (followed by [ or ]):

# a comment#[
a
multi
line
comment
]#

JavaScript compilation

From the Nim website:

“Nim includes a first-class JavaScript backend so you can target the client and server effortlessly at the same time.”

That’s quite cool, although I’m not sure how many people would actually use it. But if you want to play Browser Snake written in Nim, you can do so. This time I didn’t build it, though.

Iterators

Instead of defining a proc, one can also define an iterator. However, Nim iterators are actually more like Python generators. Here’s an example:

iterator countup(a, b: int): int = 
var res = a
while res <= b:
yield res
inc(res)

Case and underscore indifference

Nim is case- and underscore-insensitive (except for the first character).

Thus HelloWorld and helloWorld are different, but helloWorld, helloworld, and hello_world are all the same, making this valid:

proc my_func(s: string) =
echo s
myFunc("hello")

Popularity

Image for post
Image for post
On a positive note, Nim has almost 10,000 stars on GitHub

Maybe you read the title and said to yourself: Uh, I’ve heard of Nim or I’ve used Nim!

In that case, hey, I’m happy for you. However, I did try to get a bit of info on the popularity of the language, and it surely isn’t that high.

For example, Nim wasn’t even mentioned on the 2020 Stack Overflow Survey. I couldn’t find any jobs for Nim developers on LinkedIn (Location set to Worldwide), and the Stack Overflow tag for the language has only 349 questions. (Compare that to Python’s ~1,500,000, or those of a newer language, like Swift’s 270,000.)

Thus, it’s quite fair to assume most developers haven’t used it and that many have not ever even heard the name Nim.

A True Python Alternative?

I’ll be honest with you, I find Nim pretty cool.

To write this article, I went over the bare minimum, so haven’t gone too deep into it yet, but I can see myself using it for something in the future.

However, while the basic syntax is very similar to Python, it gets more complicated quite fast, which I’m sure would throw off a lot of the Python user/developer base.

Personally, I am a big fan of Python but also of statically typed languages, so for me, the performance improvement in certain cases would more than compensate for the added verbosity.

Then, through writing this article I realized: What about Go?

I’m sure many of you were thinking just that while reading, and it is a valid point. As much as the syntax of Nim might be closer to that of Python, it really is competing in the space of performant-yet-nicer-than-C++ languages, which is currently led by Go.

Fun fact: I also sneakily ran the speed test with Go. For fibonacci(40) specifically, it was as fast as C.

So, can Nim compete with Python? I highly doubt it. We’re seeing a trend of computers getting faster and programming getting easier, to the point that even if Nim offers a good tradeoff, as I pointed out, I don’t think it’s enough to take on the clean and versatile Python.

Edit: I spoke to one of the Nim Core Devs, who told me he believes Nim is more suitable for people transitioning from C++ than people transitioning from Python.

However, can it compete with Go? Maybe (if Google wasn’t behind Go). The syntax is friendly, the language is powerful, and it has better support for C/C++ features than Go offers (e.g. macros and overloading).

Maybe that’s what I should look into next?

Thanks for reading!

Further Reading

Better Programming

Advice for programmers.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store