Welcome to Day 15 of this year’s A Language a Day Advent Calendar. Today’s topic is introduction to the Nim programming language.
Facts about the language
Some facts about the Nim programming language:
- A statically-typed language
- Semi-case-insensitive identifiers (see below)
- Uses Python-like code indentation
- Appeared in 2008
- Website: nim-lang.org
Installing and running Nim
To install Nim on your operating system, consult the Downloads page and find the correct instructions. On a Mac, you can install it with
$ brew install nim
To run a program, you need to compile it first (note the
$ nim c helloworld.nim
After this, you obtain a binary executable file that you can run:
You can also do both actions in a single go (but you will also see a number of messages printed during the compile stage).
$ nim c -r helloworld.nim
Here is the Hello, World! program in Nim:
echo "Hello, World!"
If you prefer, you can add parentheses:
Variables are created with the
var keyword. In the next example, we are using a variable to keep a string:
var name = "John" echo "Hello, ", name, "!"
In this program, the type of the variable can be deduced by the compiler. In a general case, you specify it explicitly:
var name: string = "John"
You can also create constants with the
const keyword; they must be computable at compile time. You can create a variable with the
let keyword, in which case you can only assign a value once.
const x = 42 echo x
# x = 42let y = readLine(stdin) echo y # y = "another value"
An unusual feature of Nim is how it treats the names. To compare two identifiers, Nim checks if they start with the same letter, including the case, but the rest of the name is case-insensitive. Underscores are ignored.
The following program uses different variable names but they all are recognised as the same variable:
var UserName = "Alla" echo User_Name echo Username echo USERNAME
# echo username
The last example is an error, as the first-letter check is case-sensitive.
Nim uses the word procedure to describe a function, regardless if it returns a value or not. To define a procedure, use the
proc greet(name: string) = echo "Hello, ", name, "!" greet("John")
If you return a value, you can do it explicitly with the
return keyword, or simply by assigning the value to the pre-defined
proc sqr(n: int): int = result = n * n echo "The result will be ", result # return result echo sqr 5
If you don’t want to use the result of the procedure, you have to explicitly
discard it, otherwise it will be a compile error.
discard sqr 5
Nim allows overloading procedure names to obtain multiple dispatch:
proc info(s: string) = echo "This is a string" proc info(i: int) = echo "This is an integer" info 42 # This is an integer info "42" # This is a string
Computing a factorial is a quite straightforward routine:
proc factorial(n: int): int = if n < 2: 1 else: n * factorial(n - 1) echo factorial 1 # 1 echo factorial 5 # 120
Notice here that you do not need to type the
return keyword. Let us look at another version of a function that uses a range and the
proc factorial(n: int): int = result = 1 for i in 1..n: result *= i
Mutable function arguments
As a side note, you could think of implementing the factorial function using the
while loop with a decrement of the argument:
proc factorial(n: var int): int = result = 1 while n > 1: result *= n dec n var x = 5 echo factorial(x) # 120 echo x # 1
Here, the parameter
n of the function is mutable, but this means that the variable that you pass to the function will be changed after returning from it. Also, you will not be able to call a function with a literal value, for example,
From the object-oriented programming world, we first want to know how to create an object with data elements in it, how to create methods, and how to do hierarchy.
Here is a class denoting a
Person with the two fields:
age. Note the syntax with the
type Person = object name: string age: int
To create an object, call its constructor and pass the named parameters to it:
var p = Person(name: "John", age: 40)
Methods are emulated via regular procedures which take an object as the first parameter. If the method wants to change the object, mark the parameter as
proc info(o: Person): string = o.name & " is " & $o.age & " years old"
The two new elements here are the
& (string concatenation) and
$ (stringification of an object, an integer variable in our case).
There are two equivalent ways of calling such a method:
echo info(p) echo p.info
A Polymorphic example
Nim allows single inheritance, and you do it with the
of keyword. Note in the next example, that you have to make the children object references (via
ref), and the base class has to be inherited from
RootObj (otherwise you get an error saying that final classes are not inheritable).
type Animal = object of RootObj Cat = ref object of Animal Dog = ref object of Animal zoo_t = array[0..3, ref Animal] proc info(x: ref Animal): string = if x of Cat: return "Cat" elif x of Dog: return "Dog" var zoo: zoo_t = [Cat(), Dog(), Cat(), Dog()] for _, x in zoo: echo info(x)
Animal is a type object derived from the top-level
Dog are the
refs derived from
Animal. In the
type section, we also define the type of the array for storing our future objects.
It is not directly possible to define two functions such as
info(x: Cat) and
info(x: Dog) and later call
info(zoo[i]). Nim will treat this call as a call to
info(x: ref Animal). That is why the dispatch is done inside a single function. The
of operator returns true if it sees that the currently passed object is one of the tested type.
Nim supports threads and allows implementing parallel algorithms. Let us look at threads on the Sleep sort example.
Here is the full code of the Sleep sort implementation that uses threads. Simply add
spawn to call a procedure as a coroutine in a thread (whatever it is internally).
import os, threadpool proc sortMe(n: int) = sleep n * 100 echo n var data = [10, 4, 2, 6, 2, 7, 1, 3] for _, x in data: spawn sortMe x sync()
To join the threads, call
sync() at the end of the program.
Note that you have to compile this program with the following command-line option:
$ nim c -r --threads:on sleepsort.nim
It is interesting to check what Nim offers for parallel algorithms (which is experimental at the moment).
That’s it for today, but Nim is more than what we saw so far. Refer to the following resources to learn more about it:
- Official documentation with tutorials, style guides, and library references
- Let’s Explore Object Oriented Programming with Nim
You can find the source code with today’s examples on GitHub as usual.
Next: Day 16. V