Interlude 1 - Why is Nix hard?

  • unfamiliar language with mind-stretching new concepts - unless you are a Haskell (or PureScript, Idris, etc.) developer, the Nix language will take some getting used to. If you're coming from a background in C, C++, Python, Java, Javascript, C# or similar, you are used to thinking of programming in terms of sequences of instructions. Sure, objects abstract this away to some extent, but you can still find the entry point and trace what happens, step-by-step. This has the indisputable advantage of being close to the way computers actually work: in assembly language (and to an even greater extent in machine code), your program really is just a sequence of instructions being executed. Functional programming is a bit further removed from how computers actually work. To what end? The benefit of functional programming is the focus on a clean mapping from input to output without side effects - in other words, functional programming is further from the CPU and closer to mathematics. In practice, this means that Nix can be a bit frustrating to read at first, because order of execution doesn't matter like it does in, say, Python. Nix is declarative, and this is a huge advantage, but it does mean that reading Nix feels different than reading a more 'normie' language. Nis has been described as 'JSON with functions', and I find this is a helpful way of thinking about it. Of course, it also has a few quirks like 'let-in syntax', but with time and experience, the benefits of (nearly) every design choice will become clear and they will become more of a delight and less of a hindrance to understanding.
  • terribly unhelpful error messages - even the core team of Nix will acknowledge this. This is closely related to the point above; functional languages, because they are inherently less sequential, can make it harder to localize the exact cause of the error, because sometimes expressions are evaluated in a different place than where they are written in your source code, and the error then refers to that evaluation-related location, rather than the location where you would actually fix the error. The Nix core developers have this on their radar and are working on it, but it's one of the poin points that people generally learn to live with. Over time, debugging Nix is something you will get a feel for, even if the error traces are less immediately helpful than in, say, Julia or Python.
  • lacking documentation - this is getting better all the time, but it's uncontroversial to say that Nix/NixOS does not have the same caliber of documentation as, say, PHP or C#. The ecosystem can feel a bit chaotic, especially at first when you're stilling getting a feel for it.
  • slightly awkward transition phase - at the time of writing (January 2024), Nix flakes and the 'Nix command' (nix <subcommand> rather than nix-<command>) are in the awkward position of being considered best practices by the community while also being officially 'experimental'. This means that most of the official documentation focuses on the 'stable', non-experimental commands and features. There are some excellent sources that remedy this, and this is getting better with time. But the transition from 'Nix 2' to 'Nix '3.0' (not yet released, but planned to include flakes and the Nix command as first-class citizens) is not entirely painless. Still, it's probably better than Scala's 2-to-3 migration and indisputably much better than Python's 2-to-3 chaos :)