The Three Difficult Things about Nix
Tags: programming.nix
Nix is a fancy package manager, with a bunch of powerful features.
One thing it’s notorious for is its steep learning curve.
Although generally, learning to write Nix code is difficult for the same reasons a lot of other tech is difficult (e.g. you have to be aware of what’s possible, and unfamiliar things are always going to be have friction compared to what’s familiar), I reckon there are a bit more specifically three reasons why Nix is difficult.
Any difficulty someone is having when trying to use Nix almost always falls into one of:
difficulty with the Nix package manager itself.
difficulty with the Nixpkgs codebase.
difficulty with software and how it mixes with Nix’s approach.
Discussing this a bit more:
Three Difficulties
Difficulty with the Nix Package Manager Itself
When learning about Docker, you’ll need to learn about the difference between an “image” and a “container”.
Or when learning Terraform, you need to build an intuition for how the providers model and manipulate the resources being declared.
With the Nix package manager, there are also a bunch of concepts to learn.
Nix’s approach to package management is quite different to how traditional package managers have done things. So, with Nix, you end up bumping into weird terms like “derivation”, “instantiate”, “realise”.
I disagree with many who say things like “I like the idea of Nix, but wish I could write it in JavaScript”. The Nix expression language is essentially a “JSON + function (+ imports)”, similar to languages like Jsonnet (which I don’t hear the same complaints about).
But, I agree it’s fair to say that the Nix expression language’s laziness can lead to code that’s very “clever” (and hard to read). And concepts like “partially applying a function” are common in functional programming, and unusual/exotic otherwise.
And e.g. it took me some time to wrap my head around why many Nix files would open with { pkgs ? import <nixpkgs> {} }:
.
Much of the community has adopted use of Nix Flakes.
There are several things about flakes that are not very ergonomic, and not all that intuitive, although overall I think they’re not too difficult to understand, and using flakes comes with more benefits than disadvantages.
Difficulty with the Nixpkgs Codebase
Nixpkgs is somewhat comparable to the “standard library” of Nix.
A lot of Nix code will typically use common functions in nixpkgs. e.g. functions like pkgs.mkShell
or buildPythonPackage
on the NixOS wiki page for Python are a part of the nixpkgs codebase.
Nixpkgs is a large codebase. It’s not immediately clear what’s useful and available. As you dive deeper into Nixpkgs, you quickly see it’s filled with a mixture of similar-but-different and often “clever” solutions to similar problems.
I think this is responsible for a lot of difficulty with writing Nix code. (Although I think e.g. nix.dev now does a lot to ease over the rough edges of getting started).
If you provided pkgs.mkShell
in JavaScript, it would still be confusing to use.
It can be difficult to figure out how to properly access and manipulate code in nixpkgs.
The lib.customisation
lib functions are both confusing to understand and related to .override
and .callPackage
functions which are extremely common when dealing with Nix packages.
Difficulty with Software and How it Mixes with Nix’s Approach
This sorta applies more to NixOS than to Nix. (If someone runs into problems with Nix on non-NixOS, they can always just do things without touching Nix).
One of the reasons Nix is difficult is that when something goes wrong, it often requires a deeper and broader understanding of what’s going, compared to other Linux systems. – You might need to understand what nix is doing, what the nixpkgs code in use is doing, or what the system-under-nix is doing.
As a novice programmer, I could get by with copying and pasting snippets from StackOverflow (after searching for whatever error message I encountered). – Not so if you run into problems when on NixOS!
Nix doesn’t replace other Linux things (like make
), so much as it uses them in an uncommon way. – I recall reading a comment where someone’s cold shower against Nix hype was that it was “just” using make
& symlinks. – Nix and NixOS mean an additional layer that may need to be understood.
Probably the most common example of this problem is that it’s difficult to run pre-built binaries on NixOS, unlike on most other Linux distributions. On other Linux systems, shared libraries get installed into a global location, and pre-built binaries are able to find these. Whereas NixOS puts shared libraries in isolated locations, so pre-built binaries don’t run without some massaging.
It can be extra frustrating that something is easy on other Linux distributions but may be difficult with Nix/NixOS.
Other Remarks
There are sometimes difficulties people run into with Nix that don’t really fit into these categories. e.g. sometimes a tool takes an approach which doesn’t map well to how Nix wants things done. But, if someone has the above difficulties covered (they’ve got a good understanding of Nix, the relevant parts of Nixpkgs, and of what the program is doing), then the problem is more likely to feel tractable.
Another way of putting the above difficulties is “you gotta know what you’re doing (if something goes wrong)”.
And for many developers, there’s an expectation of being able to get pretty far without needing to spend a long time learning all the details. (Often this is a pretty good strategy!).
I think to some extent, some of that difficulty is fundamental.
Nix provides benefits like “will work the same way next time you run it”, and this demands it comes with a level of strictness.