Pushing the limits of the Nix language

I write about my descent into the Nix programming language and try to push it to its computational limits.

Nix, the purely functional package manager is pretty good at what it does. It evaluates things lazily for optimal performance and purely such that installations on one machine can be installed exactly the same on other machines. With Nix, comes Nix expressions which which is the technical name for the functional language that powers Nix (For the rest of this blog post, I will refer to Nix expressions (the language) solely as ‘Nix’). Now, this language is designed for package management only. It includes a few basic data types: Integers, Floating point numbers, Booleans, Strings, Lists and Sets (And of course, functions). It doesn’t allow you to create new data types or type classes (as can say, Haskell). It doesn’t have fancy pattern matching and has a very minimal “standard library”. So, the question lies: Can Nix do anything beyond package management?

Trying to do maths in Nix

Surprisingly, Nix has support for the basic arithmetic operations: addition, subtraction, multiplication and division. In addition to this, there are comparisons (greater than, less than etc.) and equality operations. So, there we go - basic maths in Nix! With this, surely we can begin to test the limits of using Nix to perform mathematical calculations.

When starting new programming languages, my normal “hello world” program is the first problem hosted on Project Euler - a website dedicated to maths and computery problems. The first problem is as follows:

Multiples of 3 and 5

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.

Find the sum of all the multiples of 3 or 5 below 1000.

Now, this seems like a problem with a pretty simple solution - In pseudo code in an imperative language (such as Java), the solution would look something like this:

sum = 0;
for i = 0 to 999:
	if (i mod 3 == 0) OR (i mod 5 == 0):
		sum = sum + i
	end if
end for
return sum

Okay, not a problem. Since this is a functional programming language, we don’t have the luxury of a for loop. Instead, Nix provides various “built in” functions via the builtins “library”, which allows us to use a function called foldl' which basically performs a reduction on a list (You can read more about folds here). In pseudo code, we have some sort of function that adds the counter value to some accumulator (similar to sum) and then “iterate” over the list of numbers from 0 to 999:

-- Definition of foldl' (for this example):
-- foldl' (a -> a -> a) -> a -> [a] -> a
-- foldl' (accumulator -> i -> result) -> baseValue -> list -> returnedValue

list = [0 .. 999]
foldl' (accumulator -> i -> (
	if (i mod 3 == 0) OR (i mod 5 == 0):
		return accumulator + i
	) 0 list

Still following? If so, great - we’ve basically got the algorithm we need for this problem! All we do is basically write that pseudo code and we’re done. Except of course, Nix doesn’t have a mod function. Not by default. There is no % operator and there is no builtins.mod function. Instead, for the luxury of performing the modulo of two numbers, I have to look towards Nixpkgs’s trivial.nix file, which has the mod function.

After adding the mod function, we’re finally ready to solve the problem and to much surprise, Nix handled it with ease. Using this success as inspiration, I work on creating a mini “maths library” for Nix, which included various functions, such as square root (Using the NewtonRaphson method for approximating functions) and the ln function (Using a summing series approximation) which showed that Nix was indeed able to compute such things.

Generating SVGs with Nix

As a proof of concept, I also wanted to find out if Nix could produce some sort of “useful” file, such as an SVG. The reason for this file choice is that SVGs can require all sorts of maths and since I’ve written some basic maths library, I could probably use that to generate various graphics using that. For those that don’t know, SVGs are basically image files written in XML which define how the image looks.

In Nix’s builtins library, there is a function called toXML which (surprise surprise) converts a Nix expression to an XML string. Unfortunately, the attributes of the resulting XML file didn’t meet my requirements, meaning I had to write this thing from scratch. Overall, it wasn’t too bad - write an XML header, write the SVG tag and then write various functions that can draw whatever I want. For example, to draw a rectangle, I used the function as follows:

rect = {x ? 0, y ? 0, width, height, rounded ? 0}:
    xVal = if x != 0 then "x=\"${toString x}\" " else "";
    yVal = if y != 0 then "y=\"${toString y}\" " else "";
    wVal = "width=\"${toString width}\" ";
    hVal = "height=\"${toString height}\" ";
    rxVal = if rounded != 0 then "rx=\"${toString rounded}\" " else "";
  in "<rect ${xVal}${yVal}${wVal}${hVal}${rxVal}/>";

Accept x and y coordinates (default to 0 if unspecified), a width and height and how rounded the corners are (not rounded if unspecified), then we apply the functions as specified and map them to the required attributes for the SVG format and then produce the resulting tag for the file. It’s that easy!

Creating Nix binaries

So, what if I wanted an executable Nix expression? For example, a file where I could just use ./myNixFile.nix and it would produce the output in my terminal window? Well, it so happens that by prefixing any Nix expression with the following, you are able to execute Nix expressions!

#!/run/current-system/sw/bin/nix-instantiate --eval
# Your Nix expression here!
2 + 2

Unfortunately, passing arguments to such a file isn’t so straight forward, you have to use the --arg option to pass them to Nix expressions:

#!/run/current-system/sw/bin/nix-instantiate --eval
x: x + 2

For example, to run a file with an argument for x, you would have to use ./myNixFile.nix --arg x 2


Overall, I think Nix is pretty powerful, despite being a mere package manager programming language which was in no way designed for such capabilities. Although all of this programming may have seemed totally pointless (which indeed, it is), it was a very quick way for me to grasp the entire syntax of the Nix language such that I can easily come up with solutions to problems in Nix.

In the near-future, I plan to explore the rest of Nix’s features, such as the Nixpkgs lib library of Nix functions and hopefully deploy a NixOS machine in the cloud using NixOps!

Written on June 9, 2019