1. 18
  1.  

  2. 7

    Using Nix does require understanding, on some level, that packages are no longer just piles of files, but capabilities which get to be isolated from other packages. This could be seen as a defect in the documentation, but I don’t think it can be rectified. Capability theory is notoriously difficult for people to approach, because it is so general and can be applied to so many systems.

    Perhaps Nix’s documentation should not introduce Nix in this way:

    Nix is a purely functional package manager. This means that it treats packages like values in purely functional programming languages such as Haskell — they are built by functions that don’t have side-effects, and they never change after they have been built.

    “purely functional” seems to have misled the author, and I can understand why; it emphasizes packages as dead and frozen artifacts which are built by interpreting other dead frozen artifacts in a sanitized environment. This is not the key feature of Nix! The key feature is that Nix sanitizes the environment according to a declared specification, and this specification limits the ambient authority which the package may absorb into itself.

    As a humble suggestion to provoke conversation, may I provide an alternative introduction:

    Nix is a capability-aware package manager. This means that it treats packages like capabilities in object-capability programming languages such as E — they are built by depictions, or expressions of source code written in the Nix expression language which explicitly enumerate their requirements, and they never access authority like networking at build time.

    1. 5

      The capability analogy is hard for me to grasp onto, but when Nix starts producing content-addressed paths, I hope that people are going to spontaneously “get it”. Packages will be easier to understand as file-system level capabilities, and the general concept of content-addressed data as file-system capabilities will spill into our awareness.

      1. 3

        Using Nix does require understanding, on some level, that packages are no longer just piles of files, but capabilities which get to be isolated from other packages.[…]“purely functional” seems to have misled the author, and I can understand why; it emphasizes packages as dead and frozen artifacts which are built by interpreting other dead frozen artifacts in a sanitized environment.[…]This means that it treats packages like capabilities in object-capability programming languages such as E — they are built by depictions, or expressions of source code written in the Nix expression language which explicitly enumerate their requirements, and they never access authority like networking at build time.

        Using Nix(os) does not require understanding anything more than it’s fundamentally different than most other distros, and that for a good reason. I sure don’t grasp everything about Nix, and I’ve been using it for years! With great success and great joy, and creating a number of packages, services and so forth.

        This constant discussion about how different and difficult Nix is compared to everything else is frankly a bit tiring. The current discourse presented in most comments about Nix seems to be that you shouldn’t use it if you don’t understand it, and that to understand it you need to barf monads all day.

        And sorry if I try to put words into your mouth here - I’m not trying to call you or your comment unhelpful or even snobby.

        Nix is awesome. Using NixOS is awesome! The fact that an easy to understand config file will generate an entire system and keep it in sync with changes in the same file - really awesome.

        The fact that all packages in the system are ‘just’ a bunch of declarations is even more awesome. Especially if one has tried packaging Debian packages a few times.

        1. 3

          Using Nix(os) does not require understanding anything more than it’s fundamentally different than most other distros, and that for a good reason.

          Yes, that’s what I was trying to talk about. The original author talks about their motivations through a functional-programming lens:

          I realized that with Nix, everything was like xmonad. I could configure things with the power of nearly a full programming language, FP knowledge in hand. This was the first thing that really caused me to love Nix.

          I opted to try NixOS again on the merit that I saw people talking about a tool caled home-manager, which after reading up on it, appeared to alleviate my former problems of doing the majority of things imperatively. Additionally, I was armed with more knowledge of functional programming as a whole, meaning I was better able to (ab)use the Nix expression language.

          However, I think that a capability-oriented lens is more useful.

        2. 2

          But they can indeed access the network, they are just supposed to have the same results each time they do it. Defining Nix by the way it is intended to be used is different from documenting what it actually does. The fact that each expression is pure is something that cannot be argued with. Interpreting the usage of that purity, and defining the system by those properties is inaccurate, since it will lead the reader to ask “wow, I wonder how they restrict network access”, and then you find out they actually don’t. In short, I think you are conflating Nixpkgs and Nix.

          1. 2

            Your point about network access is a fair one. I cannot really imagine a good substitute for “networking” in my original version, either. My original goal was to dramatically establish one of the bounds of the Nix build sandbox, but you’re quite correct that the sandbox is fine-grained and policy-driven.

            How do we define “pure” today? Certainly, Nix has a strong separation between evaluation of values and execution of filesystem effects. I would agree that Nix can fulfill many definitions of “purely functional” as an expression language. But that is not unique to Nix, and it is not what differentiates Nix from other systems. There are plenty of general-purpose languages, from Haskell and Idris to E and my own Monte, which have a strong notion of purity, of functional programming, and of purely-functional behaviors.

            Rather, what is interestingly unique about Nix is its attempt to isolate packages from one another. This sort of isolation is characteristic of capability-driven design, and we can see echoes of it appearing throughout the ecosystem. For example, container images built with nixpkgs’ dockerTools are from scratch, which limits the ambient package-authority of each container.

            1. 1

              The fact that each expression is pure is something that cannot be argued with.

              Expression can be impure in regular Nix (flakes attempt to remove most (all?) impurities). For example, built-in fetchers retrieve data during evaluation and do not require a hash. There are also impurities like builtins.currentSystem and builtins.getEnv.

              1. 1

                I thought that it was like with Haskell, that your Nixpkgs derivation would itself be pure, but its execution would be impure. But from inside Nixpkgs, there is no way to execute shell commands impurely, you have to make them part of the derivation. This would be analogous to IO in Haskell without unsafePerformIO. In Haskell, you construct your program to emit a sequence (using monad) of IO actions. But it would actually only be executed upon runtime. So you’re incentivized to make your program run without IO, because you can’t actually get the results of an IO action (unless you use unsafePerformIO). Am I wrong?

                1. 1

                  Nix does not have anything like the IO monad. Impurity is constrained by not offering many impure functions. As I mentioned, Nix has traditionally had some impure functions. However, using such impurities is are generally avoided in nixpkgs (with some exceptions, e.g. currentSystem is used and getEnv to check environment variables such as NIXPKGS_ALLOW_UNFREE).

                  Flakes disable impure functions by default. So, Nix and nixpkgs should be pure by default in the future (assuming flakes are finalized, RFC’ed, and accepted).

            2. 1

              Nix documentation should introduce it as

              A purely functional package manager and language that helps you rice at home, in production, and beyond

            3. 3

              Nix is often criticized for poor documentation. I think that’s because the UX sends the hapless user running for the docs so much more than other tools. The nix cli is a thin wrapper around a huge pile of interpreted code. If you get one of the flags wrong, you don’t get “the only options for that flag are A, B, and C”. You get “string found where int expected” in a stack trace.

              1. 3

                I would later learn that the more you buy into NixOS’s declarative model, the more utopian it becomes.

                There’s a part of my experience with Nix that I haven’t formulated in a way that completely satisfies me:

                • Nix wants to eat the world
                • Nix is like ice-nine

                I see imperative Nix as a misfeature, but since you can use it imperatively I should clarify that I mean something more woo like the essence of Nix–so “Nix” in these comparisons may function more like the adjective “pythonic” (gag).

                I mean this in good/bad/neutral ways. It’s distinct enough (philosophically?) that the boundaries between it and anything that hasn’t been (or can’t be) nixified often seem untenable (roughly: awkward in some way that will inevitably lead you to nixify it or be frustrated by your inability or its impossibility). I see this as a long-term good, but in the short term it can create a frustrating cascade or treadmill of nixification until you aren’t in regular contact with awkward boundary zones.

                1. 3

                  oh hey, that’s me!