xigoi

Nim doesn't get in the way

One of the main reasons I like the Nim programming language is that it doesn't make easy things unnecessarily difficult. Let me show a few examples.

Reading a file

A common thing one may want to do in programming is to read a file into a string. In C, one could do that like this (source):

FILE *file = fopen("file.txt", "r");
fseek(file, 0, SEEK_END);
int length = ftell(file);
fseek(file, 0, SEEK_SET);
char *content = malloc(length + 1);
fread(content, 1, length, file);
fclose(file);

Wow. So much ceremony for such a simple and common task. How about in Python, which is known for being expressive?

with open("file.txt") as file:
  content = file.read()

Much simpler. But still — why not abstract this process into a single function? This is how Nim does it:

let content = readFile("file.txt")

You might object that the C approach is better because it allows more precise control — for example, you can read only a part of a large file without having to waste time. However, Nim doesn't take that away from you — if you need fine-grained operations like open and seek, they're still available. And it's not just about reading files — the concept of abstracting away the most common operations while leaving lower-level constructs for less common operations is idiomatic for Nim.

Factorial

For another example, let's define a function that calculates the factorial of a given number. (In case you don't know, the factorial of a number n is the product of all numbers from 1 to n inclusive.) Many languages have a factorial function in their standard library, but let's pretend for a moment that they don't just to showcase a few useful features. Let's start with C:

int factorial(int n) {
	int result = 1;
	for (int i = 1; i <= n; ++i) {
		result *= i;
	}
	return result;
}

Yuck. It's the 21st century and I still have to explain to my computer how to iterate over a range of numbers? Let's turn to Python:

def factorial(n: int) -> int:
    result = 1
    for i in range(1, n + 1):
        result *= i
    return result

This looks better, but what is that + 1 doing there? The answer is that Python's range function doesn't include the upper bound. Don't get me wrong — that's a good thing. However, sometimes an inclusive range is actually needed, and then it's not Python's time to shine.

By the way, notice that in both C and Python, we're declaring a variable named result at the beginning and then return it at the end. This is actually a very common pattern in programming. And if something is very common, Nim likes to make it as easy as possible.

func factorial(n: int): int =
  result = 1
  for i in 1..n:
    result *= i

Notice that the result variable isn't declared anywhere and there is no return statement. This is Nim's cool trick to resolve the situation described above: every function has an implicit result variable that you can assign to and if you don't explicitly return anything, it will be returned at the end. And of course, this feature is totally optional — it won't bother you when you don't need it.

When it comes to iterating over numbers from 1 to n, it couldn't be easier. But if we were to need a half-inclusive range (which is needed often), that also wouldn't be a problem — just change the .. to ..<.


To comment, use this mailing list.