Here's the paper from Eelco Dolstra (founder of the Nix project) https://edolstra.github.io/pubs/nspfssd-lisa2004-final.pdf
I don't see him mentioning configuration an awful lot in there, it seems to be mostly about dependencies and multiple versions of things.
me too. I broke my system a bunch of times, and eventually just gave up. I remembered the other day that I have a lot of programs installed with it that I don't even want to think about updating. rofi changed it's configuration file format recently, and that was tied up in nix. I am too lazy to fight with it for now, so I've just disabled my config file. It bothers me every time I use it.
If the system evaluates it'll most likely work, if it doesn't you roll back a generation.
I have an Ubuntu container around if I quickly wanna mess with something.
But the downside is that debugging is impossble.
Guix uses guile, which is a Lisp so we could accomplish the same with other languages.
Reproducibility. You need a pure (side effect-free) language to ensure that a package expression always evaluates to the same value [1]. If you have a non-pure language, external factors (e.g. environment variables or a server returning a different response) can influence/change what a package expression evaluates to.
[1] That said, Nix was not completely free of side-effects either. Though this is one of the issues that Nix Flakes attempt to solve.
Pick any programming language. Now, remove access to I/O (except standard input and output), networking, time, random number generators, threads, OS syscalls, etc.
Given an input, the program will always generate the same output, i.e. it is deterministic. However, the program is still allowed to have global mutable state; the state is just encapsulated to the program's memory and execution time.
A trivial example is Brainfuck. It is most certainly not a functional programming language, but it is deterministic.
The devil is in the details, and if you miss even one case then the problems are just as bad as if you hadn't bothered at all. For example, comparing two URLs in Java is not deterministic. For another example, iterating through a set in Python will give you the elements in a different order on 32- versus 64-bit systems.
In practice it's just not practical to retrofit deterministic behaviour onto a language that was not designed for it. Even your "trivial example" isn't; Brainfuck does not standardize overflow behaviour and so the same program may behave differently on different systems.
Well, it does network access, so it would excluded.
> For another example, iterating through a set in Python will give you the elements in a different order on 32- versus 64-bit systems.
This is a good counter-point.
> Brainfuck does not standardize overflow behaviour and so the same program may behave differently on different systems.
Different implementations of Nix might have the same issue in some places. In this case, whether the language is fully defined is orthogonal to whether its behavior is deterministic; here, a particular implementation will continue to be as such.
As far as I know, Nix doesn't have a formal specification; it is defined by its reference implementation.
After all you need to be non-constant over architecture, since you need to download the correct binaries for 32-bit and 64-bit architectures.
The problem is Nixpkgs, the largest Nix program. Nix-the-language doesn't lend itself too well to programming in the large. It's a bit like writing a large JavaScript SPA without frameworks or strict coding standards.
The biggest difficulty comes from the fact that it is a dynamic, weakly-typed language, which can lead error messages in configuration to point you toward library code rather than your config file. This is a pitfall shared by many of the languages that are commonly suggested as replacements or alternatives.
There's been work to solve this by adding gradual typing (like in Typescript) to Nix, whose most promising iteration atm is (imo) a Nix-like language called Nickel, which is almost ready for preview by the community.
The other, more minor, issue is tooling, which is being addressed by emerging LSP work for Nix.
(Scripting is done in Nixpkgs, mostly via Bash.)
The beauty of Nixlang, imo, is that for simple use cases it really feels like a dead simple configuration language, which is only possible with a declarative language.
The functional character and Nix's laziness were important in the early design of Nix. The latter may still give Nix some nice performance characteristics. But since the initial Nix design, Nix has become eager in some places, and Guix is implemented as a lazy DSL embedded in an eager language, so I guess we could implement something like Nix on top of an eager scripting language.
Imperative scripting languages might not be as well suited for the kinds of overrides that Nixpkgs and NixOS use, afaict. But imo aside from its novelty, the Nix language is nice to use because it's really simple and totally declarative, which is what you expect from a configuration language.
> You don't need all values of your language to have lazy semantics
Once upon a time (no longer, I think), Nix was 'maximally lazy', so I think historically at least, there has been a question of how essential laziness is to the design of package managers in the functional paradigm. Since then I guess we've seen what you describe (some targeted laziness) is all that's required in both DSLs.
I've edited my comment above to better reflect Guix's design and stop spreading the 'Guix is eager' meme. :)
There’s an open RFC/PR to bring NixOS style modules to Nixpkgs for package management (with derivations being the primitive).