Mostly it provides guarantees about your system. It guarantees that anything not in your config is not on your system (for the most part). It guarantees that everything builds successfully before it applies any changes. Most of the filesystem is immutable so it forces you to use the configuration.
There are some other advantages: rollback to a previous state at any time, enable complex system services with one line in your config file, install multiple versions of tools on your system without conflicts, install programs without root (using home manager), build VMs and Docker images, pin the versions and dependencies of all packages for reproducibility, patch software with your own tweaks natively. You can also put a Nix file in a code repo and easily share your build dependencies with other developers.
Yes, that’s exactly the kind of thing it’s great at.
Nix stores all builds of its packages in a separate “store” on the machine, each identified by the hash of the build (“derivation”). When you activate your config, it basically symlinks the version you want into your PATH.
If your project needs a specific version, when you activate a Nix shell or script it will check your store for that version and activate it. If it doesn’t exist, it will fetch it (or even compile it from scratch if you need to).
Part of the magic of asdf is that it reads a `.tool-versions` file from anywhere above your current directory and applies the correct version of each tool based on the listed value. (You can also configure a global default version for each tool in case your directory tree doesn't have a .tool-versions file with an entry for the tool you want to use.)
Does Nix allow for a similar workflow? It's really nice to be able to move between projects and automatically have the correct tool versions configured without having to run any special command.
I think the nice thing about project-specific tooling like this is it allows getting started with the project very easily. (Rather than copy-pasting "apt-get <whatever>").
To add to what rgoulter said, you can use your default system configuration plus whatever the project says you need (and the project's shell will override any conflicts with your default).
However, if you really want to make sure that you're not including anything from your system, you can have direnv pass the `--pure` flag, which will then include only the dependencies from the project and nothing else. This is helpful to make sure that you're not accidentally relying on something already on your system when you declare the project dependencies.
There are some other advantages: rollback to a previous state at any time, enable complex system services with one line in your config file, install multiple versions of tools on your system without conflicts, install programs without root (using home manager), build VMs and Docker images, pin the versions and dependencies of all packages for reproducibility, patch software with your own tweaks natively. You can also put a Nix file in a code repo and easily share your build dependencies with other developers.