Introducing Brioche, a new Nix-like package manager written in Rust


(Not affiliated with the project)

I wish it said more how it is like/unlike Nix.

I am all for people making new Nix-like things in better programming languages. However, my default assumption is that many people saying Nix is too hard / too annoying are missing that Nix’s power and nix’s want-to-control-everything annoyance come from the same place: complete purity, no exceptions.

Derivations always felt weirdly rigid.

Yes! That’s the point!

I didn’t like that the Nixpkgs repo as a whole is versioned as one unit instead of individual packages.

Depending on how one interprets this, this is easier

  1. A mere loose convention (we could organize our package repos in many different ways, though I am personally skeptical of there not being a single root repo that pins exact versions of dependencies)

  2. A hard consequence of “deviations being rigid”

I get the impression that this person wants “Cargo, but for my whole computer”, and while the sounds nice and friendly, I am sorry it’s a no-go. The world of open source software is way too crufty and shitty, and you need a “bad cop” tool like Nix to discipline it and force it behave.


From what I gathered:

  • obviously, unstable, preview, not ready, etc
  • TypeScript, rather than nix for describing derivations
  • Rust, rather than C++, for the implementation
  • “relocatable” artifacts: output doesn’t know it’s eventual location, can only use relative paths
  • CA derivations
  • geared for polyrepo with lockdiles

I think unless they rely on a bunch of patches (probably way more than nixpkgs requires to make things work in Nix store and profiles structure), this is really not that easy to accomplish.

1 Like

They do something quiet interesting here:



Thanks for sharing. IMHO, the primary goal of package managers is to simplify software consumption for users. The fewer layers we have, the better the experience. I’m always curious about the motivations behind creating a new package manager similr to or on top of an existing one, but I have yet to come across compelling reasons for this approach. If someone decides to build another package manager, it likely means that the existing solutions were not “good enough”. But in that case, why not combine efforts and contribute to improving the existing ones instead?

I can’t help it, but it always makes me think to


That said, I might sound critical, but I sincerely wish you the best of luck with your project.

As I’ve said elsewhere, the two non-trivial observations here are:

  • One can’t change architecture of established projects, because that requires too much coordination and comes with too much risk.
  • Effort is not zero sum, empowering new contributors gives better results than trying to direct the effort of existing ones.

What people who post this XKCD don’t mention is that ackshually, it’s a good thing to have many standards, and thus competition.

The communication overhead from cooperating is far from zero,
and jumping into an existing project, learning why it is the way it is, isn’t easy.

Often competition is the best way of cooperating.
Brioche will hit issues that we don’t have because of
our design, and will fix issues that we do have.

Not to mention you could feasibly “wrap” Brioche in Nix, if you wanted to.


There are some things to like on this list, like using Rust for the implementation, but also the idea of relocatable artifacts (though I’m not sold on the specific mechanics of that feature).

However, I think that despite some complaints here and there about Nix-the-language, the use of a Turing Complete language rather than a designed-for-purpose DSL is a fatal mistake that also happens to violate the rule of least power. I’ve seen that approach being used before (Scala’s SBT for example) and it just becomes a Big Bag of Hurt™. On a project-by-project basis at least it definitely doesn’t become more accessible despite the use of a popular language like Typescript, because the non-DSL-ness of the language often means that verbosity and cryptic ad-hoc directives becomes an inseparable part of the build script. In other words: can you express your build with TS? Yes, of course. But can you do it in a way that the stuff that matters pops out with as little incidental complexity as possible? I have never seen that happen at scale when using a Turing-complete language for build scripts.

I think the complaints about Nix-the-language mostly just stem from people not being informed about how it all works, combined with the expectation that things work the way they are used to in other environments.

In that way it really reminds me of about ±20 years ago when people were having a hard time being convinced that functional programming was a good idea (e.g. lots of people didn’t see the value of closures in programming languages). That resistance wasn’t reflective of the merits or demerits of FP; Rather, they reflected the inertia of people being stuck in a purely imperative world.
I think the same holds true for Nix, and just like 20 years ago, the answer is not to backtrack, but to educate people on how they can get their tasks done the Nix way.


To be pedantic, non-turing completeness is a vacuous property, which doesn’t tell anything useful about a computing system. The core mathematical fact here is that any algorithm, whose runtime is bounded by a primitive-recursive function, is itself primitively-recursive.

That is, if you computation runs faster than, say, (2**n)**n, you don’t need Turing complete language to express it.

Or, in other words, any language can be trivially patched to become non-Turing complete by just adding a total counter of instructions and forcibly halting when it’s greater than a billion billions.

The additions I’m talking about in practice aren’t found in theory books. The difference is mainly incidental complexity, occurring in every script written in such a language, and thus producing unnecessary pain for every user of the language.
As such, I use Turing-completeness mainly as an antonym for DSL, since those are the 2 main classes of programming languages one can find, at least in terms of purpose (as opposed to what paradigms such a language uses for example). That contrast technically isn’t sound (there are DSLs that are Turing-complete) but for my purposes it’s a useful one.

It’s also not even that a Turing-complete language cannot fit nicely with the task of dependency and build management. But if the language wasn’t written with that purpose in mind (and doing so would effectively turn it into a DSL that happens to be Turing-complete), incidental complexity seems to follow in spades.

I’m not convinced of that TBH. Most of the software written sits squarely within the P complexity class (such algorithms are definitionally at most O(n ** k) for some integer k and large enough n). Yet the solutions tended to be written in languages that in practice were Turing-complete.
This implies that Turing-completeness either was completely serendipitous (hard to believe after Turing’s paper decades earlier), or there was intention and purpose behind that. Which might be as simple as “we wanted languages for which it was known that we could express the solutions to any of our problems in them”.

Which sounds more likely to you? :slight_smile:

Arguably, for making file systems trees appear, all we would ever need and want are finite computations. The ideal language for that would be a proof assistant such as Lean, giving you the power of dependent types and the predictability of total functions.


My real-world experience confirms.

Me, early on, learning NixOS but not understanding Nix:
This is weird and confusing and I wish they had used an existing language.

Me, having taken the time to actually learn the Nix language:
Wow, Nix is awesome! So well designed for it’s purpose!

I think relocatable binaries are a lot simpler than people assume.

RPATH could use $ORIGIN and reference the store via a relative path.
That way the whole store could move wherever
( think that’s what does FWIW)

1 Like