Efficiency First — A Language Built for Performance
Check out this excerpt from Dominik Picheta's Nim in Action and learn about the various perks of Nim, the general-purpose programming language that is designed to be efficient, expressive, and elegant.
Join the DZone community and get the full member experience.Join For Free
This article is excerpted from Nim in Action, https://manning.com/books/nim-in-action.
In the past century, technology has been evolving at a very fast rate. The last 70 years in particular saw computer hardware steadily improving together with programming languages. Programming languages have a long history of evolution, very few that were around in the 50's are around now. Thousands are created every year, some only used by but a handful of people, others becoming adopted by millions of programmers around the world to become mainstream.
When the first computer was invented, the primary method of programming was writing machine code specific to your computer's CPU. Machine code is still used today, but it is not written by programmers; in most cases it is generated by compilers because writing large and complex pieces of software using machine code would be an extremely time consuming and draining experience.
The first programming languages were designed and implemented to make software development easier and less error prone; to this day, programming languages try to strike the perfect balance between ensuring that programmers make the least mistakes possible and that programmers are as efficient at writing software as possible. Each new programming language builds upon older programming languages, adding tiny innovations along the way or combining features from multiple programming languages to create a new and better programming language.
Nim is still a relatively new programming language. The language is still not fully complete, but core aspects like its syntax, the semantics of procedures, methods, iterators, generics, templates, and more are all set in stone. Despite its youth, there has been a significant interest in Nim from the programming community. This article includes information about some of Nim's primary features and what the Nim programming language is.
What is Nim?
Nim is a general-purpose programming language designed to be efficient, expressive, and elegant. These 3 goals are of course difficult to achieve at the same time, so the designers of Nim have given each of them different priorities. Efficiency being the most important and elegance being the least important.
The development of Nim began in 2005. Andreas Rumpf developed Nim on his own for the next 3 years before releasing the source code and starting to develop Nim publicly in 2008. It wasn't long before the project gained support and many contributions from the open source community. Nowadays the development of Nim is still lead by Andreas Rumpf, with many volunteers around the world contributing code via pull requests on Github.
Use Cases for Nim
Nim was designed to be a general purpose programming language from its inception. As such, it was designed with a wide range of features to make it usable for just about any software project. This design makes it a good candidate for writing software in a wide variety of application domains ranging from web applications to kernels.
The language does indeed support practically any application domain. But, this does not mean that it is the right choice for everything. Certain aspects of Nim make it more suitable for some categories of applications than others. This does not mean that these applications cannot be written using Nim; it just means that Nim may not support the code styles that are well suited for writing those kinds of applications.
One interesting aspect of Nim's compilation model makes it particularly well suited to systems programming. You will find that Nim source code is compiled into C code first. C is a pretty old but well supported systems programming language; because of this, Nim allows more direct and easier access to the physical hardware of the machine. As such, Nim is very well suited to writing operating systems, compilers, device drivers, embedded system software, and more. Despite the fact that Nim is still a new programming language, there are already many examples of such projects. For example, a very simple operating system called NimKernel is available on Github here: https://github.com/dom96/nimkernel.
Applications written in Nim are very fast because Nim as a whole values efficiency the most and provides features which make optimizing code easy. This goes hand in hand with Nim's soft real-time garbage collector, which allows you to specify the amount of time that it should spend collecting memory. This is important for games development, where an ordinary garbage collector may slow down the rendering of frames on the screen, if it takes up too much time collecting memory. It is also useful in real-time systems which need to run in very strict time frames.
Applications which perform input/output operations such as reading files or sending data over a network are well supported by Nim. Web applications for example can be written easily using a number of web frameworks like Jester. Nim's script-like syntax together with the powerful asynchronous input/output support make rapid development of these applications easy.
Nim is particularly well suited to writing command-line applications because it does not require any runtime dependencies (unlike Java with the JVM or Python with the Python interpreter). Applications can be compiled to be standalone without any dependencies easily and distributed across many machines without any issues. One example of such an application that has been written in Nim is Nimble; it is a package manager for Nim and allows users to install packages containing Nim libraries and applications.
Hopefully, you now know a little bit about what Nim is, its history, and some of the applications that it's suitable for. The next sub-sections demonstrate some of Nim's features and talk about how Nim works.
In many ways, Nim is very innovative. Many of Nim's features can't be found in any other programming language. If you enjoy learning new programming languages, especially those with interesting and unique features, then this is the language for you.
In this section, we will look at some of the core features of Nim, in particular, the features which make Nim stand out from other programming languages.
Arguably, the most practical and unique feature of Nim is its extensive metaprogramming support. Metaprogramming was by no means exclusive to Nim, but there is no other programming language with metaprogramming that is so extensive and at the same time as easy to pick up. For instance, if you're familiar with Lisp then you might have some experience with metaprogramming already.
Metaprogramming in Nim is special because languages with good metaprogramming features typically belong to the Lisp family of languages. If you are already familiar with the likes of Java or Python, then you will find it easier to start using Nim than Lisp. You will also find it more natural to learn how to use Nim's metaprogramming features rather than Lisp's.
Metaprogramming is generally an advanced topic. One of the main benefits that metaprogramming provides is the ability to remove boilerplate code. Metaprogramming also allows the creation of domain specific languages, which can be used to include mini-languages inside Nim source code. For example:
html: body: p: "Hello World"
The mini-language above specifies a bit of HTML code. In this case: <html>, <body>, and <p>Hello World</p> will be returned by the code above. Metaprogramming in Nim allows you to define such languages and mix them freely together with your ordinary Nim code. Such languages have many use cases, for example, the one above can be used to create HTML templates for your web apps.
Another interesting and likely unique feature of Nim is style insensitivity. One of the hardest things a programmer has to do is to come up with names for all sorts of identifiers like variables, functions, and modules. In many programming languages, these names cannot contain whitespace, so programmers were forced to adopt another way of separating multiple words in a single name. As is often the case, multiple differing methods were devised. The most popular being snake_case and camelCase which are still debated to this day. Nim allows you to use snake_case even if the identifier has been defined using camelCase, and vice versa. This allows you to write code in your preferred style even if the library which you are utilizing uses a different style for its identifiers.
import strutils # The `strutils` module defines a procedure called `toUpper` echo("hello".to_upper()) # We can call it using `snake_case`... echo("world".toUpper()) # ... or, as it was originally defined using `camelCase`.
This works because Nim considers the identifiers to_upper and toUpper to be equal. It does not differentiate between case or the underscores when comparing identifiers.
Powerful Type System
One of the many characteristics which differentiate programming languages from one another is their type system. The main purpose of a type system is to reduce the possibilities for bugs in your programs. Other benefits which a good type system provide are the possibility of certain compiler optimiZations, better documentation of code, and more.
There are many different categories used to classify a programming language's type system. The main categories are the static and dynamic type systems. In most cases a programming language is not on the extreme end of either of those type systems, it incorporates ideas from both. This is done because both require certain trade-offs. While static typing finds more errors at compile-time, it also decreases the speed at which programs can be written. Dynamic typing is the exact opposite.
Nim leans towards static typing. But, unlike some statically typed programming languages it also incorporates many features which make development fast. Type inference is a good example of this. Types can be discovered by the compiler without the need for you to write them out yourself, unless you choose to write them out of course. Because of that, your program is still bug free and your development speed is not hindered. Nim also incorporates some dynamic type-checking features such as runtime type information which allows for dynamic dispatch of functions.
One way that a type system will ensure that your program is free of bugs is by verifying memory safety. Some programming languages like C are not memory safe, because they allow programs to access memory which has not been assigned for their use. Other programming languages are memory safe, at the expense of allowing programs to access low-level details of memory, which in some cases is necessary. Nim combines both, it is memory safe as long as you don't use any of the unsafe types such as ptr in your program, at the same time the ptr type is necessary when interfacing with C libraries and supporting these unsafe features makes Nim a powerful systems programming language.
Lastly, one of the most important features of Nim's type system is the ability to utilize generic programming. Generics in Nim allow for a great deal of code reuse without sacrificing type safety. One of the things they allow is to specify that a single function can accept multiple different types. You may for example have a showNumber procedure which displays both integers and floats on the screen.
proc showNumber(num: int | float) = echo(num) showNumber(3.14) showNumber(42)
In the listing above, the showNumber procedure accepts either an int type or a float type. The `|` operator is used to specify that both int and float can be passed to the procedure.
I have mentioned in the previous section that the Nim compiler translates source code into C first, and then feeds that source code into a C compiler. There are many practical advantages that this compilation model possesses. In this section I will discuss some of them.
The C programming language is very well established as a systems programming language and has been in use for over 40 years. C is one of the most portable programming languages, with multiple implementations for Windows, Linux, Mac OS X, x86, amd64, arm and many other more obscure operating systems and platforms. C compilers support everything from a super computer to a micro-controller, they are also very mature and implement many powerful optimizations which makes C very efficient.
Nim takes advantage of these aspects of C, including its portability, widespread use, and efficiency.
One specific benefit of compiling to C is that unlike programming languages which run on the JVM, such as Java, Scala or Clojure, and interpreted programming languages such as Python, Ruby and Perl. Nim software can run without any dependencies. This makes distribution of your applications very easy. One situation where this is a huge advantage is when deploying web applications to multiple web servers, the lack of dependencies means that you can simply upload your web application's binary executable and run it without needing to install any additional dependencies on the server. This also makes Nim very well suited to embedded systems where resources such as memory and disk space are limited.
Compilation to C also makes it very simple to use existing C and C++ libraries. This is done by first wrapping those libraries, wrapping can be done manually or with a tool called c2nim which generates these wrappers automatically.
There are very many libraries written in C and C++, many of which are very popular. You can use all of these libraries very easily from Nim. This works both ways, so you can also write libraries which can then be used from C and other programming languages.
C and C++ both require you to manually manage memory, carefully ensuring that what you allocate is deallocated once it is no longer needed. Nim on the other hand manages memory for you using a garbage collector. Garbage collectors are considered by many to be inadequate for certain application domains like embedded systems and games. For this reason, Nim supports a number of different garbage collectors with different applications in mind. The default garbage collector is real-time so it allows you to specify the amount of time it should spend on collecting memory. It is well suited for real-time systems and games, because it will not pause your applications for unknown periods of time. The garbage collector can also be removed completely, giving you the ability to manage memory yourself.
There is a lot more to Nim, not just the unique and innovative features, but also the unique composition of features from existing programming languages which makes Nim as a whole very unique indeed.
To learn more about the Nim programming language see Nim in Action at https://manning.com/books/nim-in-action and save 39% with discount code pichetadz.
Published at DZone with permission of Dominik Picheta. See the original article here.
Opinions expressed by DZone contributors are their own.