@jakehamilton
So, I’m not nearly done, at lot of it looks fantastic and I’m really impressed. In terms of criticism one of the first things I run into is “where is lib defined?”. For example
- first I went to src/staging/stage0
- then that lead me to builders
- not sure what
kaem
is, so I went to one of the other builders
- got to
lib.modules.override.default
but I don’t know that behavior and I don’t/can’t know where that is defined, because lib is an argument and we (generally) don’t know everywhere this function is called
Which is a bigger design principle I think we should discuss.
_
When we read lib.doSomething
I think we pretty much all expect it do the same thing every time. But lib.modules.override.default
could return null every time for all we know; it is an argument after all. Instead of importing lib (like most languages), when we have it as an untyped argument we loose all ability to locally/statically reason about the code. As a reader, we can’t even assume lib.modules.override.default
will be a function.
Readbility is one thing, but more importantly it makes code really really hard to refactor/maintain because, for example, if there is a null check on lib.modules.override.default
then we can’t really ever remove that null check, because maybe – somewhere – an argument does cause it to be null. “Anything is possible (thanks to arguments)” might sound nice, but it does not help with understanding, debugging, and fully-testing code.
_
I know nix likes to be able to overwrite everything, that way (in theory) people don’t need to fork in order to modify. But after years of this approach, I think we can say it hasn’t really worked in practice. Perfect exampke is @ VlinkZ being unable to give an overlay of core stuff on nixpkgs because it breaks so many things. Even though he’s plenty experienced in using Nix.
Its easier to fork and modify, than it is to surgically try to override/hack-in behavior. So I think, at least for foundation, its okay to loose that benefit. If someone wants to change foundational behavior, just fork.
_
The alternative, I think, is having lib as a subrepo or submodule (so that we can have a direct import of lib rather than lib as an argument). I know that goes against my original plan, but hey, that’s good. It means the prototypes are doing their job at changing my opinion/estimates. I now think a subrepo is the best way to make foundation maintainable and easy to reason about for new members, while still keeping lib
pure and isolated in its own repo.
I think we could avoid the need for subrepos if the builtin import
allowed for importing URLs. But that’ll be a topic for another time.