(Basic) Thoughts on Nim

Warning: this is a very opinionated post. I’m not claiming I’m right. I’m describing my observations.

This is a great time when it comes to new, fun programming languages. Languages like Rust, Zig, Odin, Nim, Jai bring something new to the table. Those are only the languages I’m keeping tabs on. There is way more projects worth following.

This post is about Nim. I use a lot of Python and I enjoy it less and less everyday. It’s a good language with a great standard library. The community is thriving with great projects. Python2 has been put in the freezer which means that soon there will only be one, proper Python version. There is a lot of fundamental things about Python that just don’t overlap with my opinions.

I’d like to switch Python for Nim.

This post is not only about Nim. It’s also about a few programming trends that pop up in many languages and discussions. Programming community collectively decides what’s good and what’s bad based on the features they implement in new programming languages. Natural selection in programming world.

Programming languages are there so we can tell computers what to do. Well designed language makes the process easy for the coder and makes the job of generating performant machine instructions easier for the compiler. Python puts emphasis on the coder and doesn’t care about performance as much. Nim successfully satisfies both requirements.

It’s important to point out that language’s design impacts the decisions made by the designer (coder). It’s not only us, programmers, who are designing and writing the code with our tools. The tools inform our decisions.

If all you have is a hammer, everything looks like a nail.

First concept, that’s very important in programming, is mutability of your data. You make assumptions about the execution flow, based on your knowledge of the data’s state. You’re simulating the code execution in your head while writing instructions. If your assumptions are wrong then your solutions are wrong. If you know that a = 4 then adding '1' to 'a' will give a result of '5'.

We’re dealing with complex systems. The difficulty comes from the fact that you are unable to simulate the code execution flow in your head because you can’t hold all the variables that impact the result in your head. Programming language constrains the amount of possible scenarios. If there is a piece of data that should never change we declare that as a constant. That way we know its value and we can be certain that won’t change during execution.

Nim provides 3 ways of declaring your variables (data).

  • var - this piece of data can be freely modified.
  • const - this piece of data is known at compiled time; it never changes.
  • let - this piece of data isn’t known at compile time. It needs to be established when the program executes. As soon as that value gets established, no further writes to that piece of data are possible.

A variable and constant are a feature of (probably) all of the programming languages. What Nim brings to the table is the let keyword. Personally, I haven’t seen that in any other language.

A good example of usage is getting the input from the user. You ask the user once, and that answer is stored till the end of the code’s execution. You know it won’t be modified.

Another trend that is prevalent in new language is the departure from the OOP (Object Oriented Programming). It’s the backbone of one of the most popular languages in the world, C++. C++ is not a good ambassador of that paradigm, for multitude of reasons.

OOP binds data with functionality, which brings some upsides. The consensus seems to be that it is a net negative. OOP goes hand in hand with Inheritance, which also looses its appeal when compared to Composition.

Uncoupling the data from the functionality deeply impacts the design of the modern languages. In Nim the complex data type is usually represented by object type or tuple type.

Those two differ slightly. An object is a type… and that’s that. Just a bag of different types organized together. tuple is identified by its structure:

Unlike object types though, tuple types are structurally typed, meaning different tuple-types are equivalent if they specify fields of the same type and of the same name in the same order.

That would mean, you can have two, differently named, tuple types. As long as their internal structure is the same they can be used interchangeably.

I can’t tell you why this is useful. The level to which you understand the solutions is as good as your experience with the problem. I can’t say I’ve experienced the problem that this feature solves. I will look for the rationale.

The final feature of Nim I want to mention is the array type objects. Since Nim cares about performance, it gives users two collection types: arrays and sequences. The first one is a typical, compile time known, array. Its length and the data type it holds is known at compile time, which means the memory requirements are also known. That means it can be stored on the stack. The sequence is a dynamic length collection. Its memory requirements might change. In Python that would be a list.

Then there is the openArray. It can only be used as a function parameter. Function like that accepts both arrays and sequences. That way you can write your code for a collection of any size but execute it on static arrays and dynamic sequences.

This is one of those posts in which I try to find gaps in my knowledge while writing. As I’ve mentioned before, the understanding comes from seeing the bigger picture: the problem and its solution. I don’t understand the benefits of the tuple type. That doesn’t mean that there isn’t one. Nim was created by smart people. There certainly is a good explanation and an opportunity to learn.