As you know, I've been migrating my sytems to guix, which at the moment consists of packaging things that aren't yet present. I'm starting to wonder if the current standard of putting systems together is wrong. I know, we've been doing this way since the beginning, and saying this is wrong is perhaps too strong a statement. But maybe it is not.

What is a system?

Before I make a statement such as "The current standard of putting systems together is wrong.", I should probably define what is meant by system. I am using the word system to refer to a thing composed of a kernel and an operating system composed of packages. I'm thinking of a Linux kernel, with a GNU operating system, but the arguments can be abstracted to UNIX-like systems, such as any of the proprietary ones left (e.g. AIX, Solaris, or HP/UX), the *BSDs, and possibly systems around the GNU Hurd. For sake of concreteness, I'll assume a Linux kernel and a GNU operating system, typically any mainline GNU/Linux distribution available. The vast majority of my GNU/Linux work has been with Debian, so much of this may be colored by such experience.

A system is then a Linux kernel and a GNU operating system, which is composed of packages. The Linux kernel is also usually packaged along with the operating system and there is typically a bootstrap process which facilitates installing the operating system and kernel onto a machine, either physical or virtual.

So by saying the current standard of putting systems together is wrong, I'm really getting to the point that the way we package things is wrong.

Why are mainstream package managers doing things the wrong way?

In short, they depend on /usr and its children to maintain state. What's wrong with that? /usr is mutable and its mutability is not strictly managed by the package manager. Although package managers allow mutation in a controlled matter, they largely ignore users. If I as a user am developing a python application that depends on a particular version of a python library and the system administrator upgrades the python library to a newer version that has a different api, my application just broke.1 If I read the FHS, it says /usr/local is reserved for local software, or by de facto standards software that is not part of the operating system. It doesn't say how to handle cases where software in /usr/local depends on packages in the operating system or specific version of packages in the operating system. There typically doesn't seem to be a way of marking operating system packages as required by locally installed software or handling the case of having multiple versions of a given package installed.

Is there a better way?

Probably. Typical package managers are imperative, maintain meta-state (e.g. which packages are installed, at which versions, and dependencies) database and keep state in /usr.

Instead of an imperative approach (e.g. how to install package X), can we use a declarative approach (e.g. install package X)? Can we also remove state from the system and have state contained within the package definition? The answer to both questions is yes. I'm not going to attempt to argue the reasons in this blog post, because I wouldn't be able to so without spending much more time than I would like. Instead I will refer you to Eelco Dolstra's PhD thesis, The Purely Functional Software Deployment Model. For a much lighter read, see NixOS: A Purely Functional Linux, which speaks to building an entire stateless operating system.

  1. I know about language specific package managers (.e.g. pip, gems, cpan), I don't think they help outside of the single user case. ↩