I'm not convinced flake inputs should exist

It seems to me like you can do everything flake inputs can do without using flake inputs,
via builtins.fetch*, and it would be less problematic too I’d think. You wouldn’t have a huge flake.lock, but some niv-like thing, but still using flakes to get proper purity.

3 Likes

would you mind expanding on problematic parts addressed with this switch?

2 Likes

Flakes have multiple issues, and all of them seem to stem from the existence of inputs.
There is the issue of “nixpkgs” bloat, “nixpkgs” appearing many times again and again.
This is because of the design “forced” upon us, where a library specifies a nixpkgs when it needn’t specify any at all (except for testing).
If you didn’t use inputs, it would be natural to make the flake a function that takes a nixpkgs as argument.

It would also mean that we don’t fetch inputs we don’t use, inputs for transitive dependencies aren’t included in your source repository, etc.

4 Likes

Channels still work. I think channels should be the recommendation for development work. I have <nixpkgs> pointing to a git worktree in my nixpkgs repo and it works great, I make changes in the worktree and get immediate results.

<nixpkgs> is a lookup path, this has nothing to do with channels. The equivalent of channels in the flake world is the registry.

4 Likes

It’s unclear to me what parts of Flakes you expect to be left over after this change?

Maybe for a single developer but not for a team but even then I don’t thank they are a good idea.
Something like Niv or another way to pin your inputs.

This sounds to me that it would make sense to have “development” and “production” inputs and only the production inputs would get passed to another project by default.

The schema, the CLI, the purity.

1 Like

lol, what schema? Purity is a bit of an anti-pattern if you ask me. What is it for? Is it for organization? Nope. Isolation? You can achieve that without eager copying the world to the /nix/store by just maintaining some discipline (and maybe a few tests to be sure). Is it for well defined scopes? Haha, yeah right. Reducing boilerplate? Guess again…

I mean purity is the whole point of Nix. FWIW you don’t have to copy the world to /nix/store, and there are PRs to this effect being worked on right now (lazy trees etc.).

4 Likes

This is a neat idea.

It always felt like flakes were trying to do three things:

  1. Standardized format for out-of-tree packages
  2. Lockfiles (not because we need them, or want them, but because they’re so trendy!)
  3. A package registry (even trendier!)

As far as standardized formats go, I think package.nix from by-name is a better solution. It would be really cute if there were a variant of the nix CLI that let you use all the flake CLI syntax on package.nix files instead of flake.nix files.

3 Likes

In hindsight maybe, but you could retroactively reframe the definition of flake.nix in that direction. I still think I prefer flake.nix, because it’s a “snow flake” (since “nix” is “snow” in Latin).

Sure I should have been more clear: “flakes version of purity”. I get the lazy trees PR is being worked on, but it’s been in that stage for years now, also. I also can’t help but wonder if it is actually necessary. Nix is a lazy language, there are such things as chroot’s and namespaces. Do we really want to break Nix’s laziness when it comes to what it copies to the store when we could, e.g. just isolate the evaluator itself?

Just saying, there may be simpler, more useful ways to do what flakes are trying to do in a way that is perhaps more naturally compatible with Nix’s design, and doesn’t require an entire virtual file-system layer.

4 Likes

It seems to me like you can do everything flake inputs can do without using flake inputs

You cannot conveniently update dependencies without copying hashes to and fro. You cannot easily override dependencies in code you fetch. The only real design mistake with flakes, from my point of view, was the inclusion of the flake registry. That’s just a re-hash of the nightmare that is channels.

6 Likes

I think having a mechanism for declaring and defining “inputs” across projects systemically, in a pre-defined structure, is actually good, regardless of flakes. In particular I very much sympathize with the view, flake: add Nixpkgs default arguments as input by roberth · Pull Request #160061 · NixOS/nixpkgs · GitHub, that flake inputs turn out to implement a dependency injection system, like callPackage does (if even accidentally). I think this touches a problem of utmost importance: preventing the sprawl of ad hoc vendoring in Nix projects.

In Nixpkgs we mend the vendoring in upstream projects forcing everything to live in the same fixpoint and sandboxing the build. However, when using an out-of-tree Nix project we have no sandboxing mechanism to ensure this project doesn’t spawn extraneous Nixpkgs (e.g. with an extended unfree predicate) instances or pull in more projects we’re not aware of. With flakes, all of that complexity becomes visible in the lock file, and you can also attempt implementing a form of a fixpoint (inputs.${x}.follows) to reduce it. I absolutely do wish to have some form “purity” for this, the “sandboxing” for evaluation.

Ironically, while flakes happen to partially address the issue, they might have been keeping us from designing and adopting a mechanism specifically for the task…

3 Likes

One thing you can’t do is managing transitive dependencies:

You depend on A and B. A depends on C1, B depends on C2, where C1 and C2 are different versions of the same C.

There are two choices: either you duplicate C1 and C2 and use both versions at the same time, or you unify them somehow into the single version.

To do this, you need some component that looks at the full set of input constraints from all transitive dependencies, and then solves the constraint set to find the actual set of inputs to use.

To clarify, I am not saying that flaks actually solve this problem, just pointing out that the hard problem here is not “how I get my inputs” but rather “how my inputs get their inputs in a way that doesn’t lead to gratuitous duplication”.

That’s a very optimistic assessment, have you seen actual nix code or how people mindlessly copypaste?
I’ll bet you 100 (insert currency) we’d have people using import <nixpkgs> { ... } instead.
If you think that’s nuts, we already have people doing exactly that in flake.nix.

Well there’s a real funny bug with builtins.getFlake, which was discussed in nixos-option: rewrite as a nix script by FireyFly · Pull Request #313497 · NixOS/nixpkgs · GitHub. Not that it can’t be mitigated, of course it can by explicitly specifying that it’s a git-type flake, but it’s a subtle error that will just bite you later. And now, knowing that getFlake works differently than flake inputs, I can no longer consider that getFlake (or its cousin fetchTree) is an actual drop-in replacement.

That’s shocking that it’s considered unintentional; proliferation of nixpkgs instances is the biggest reason for slow eval, so we certainly need such an injection system (flakes or not), and simply - isn’t follows just dep injection by nature? What else was it meant to be used for?

1 Like

I was a long time non-flake user specifically because I like the old CLI output.

I did eventually give in…tbh I’m not sure what I gained really aside from just better support with other flakes…
(I guess importing nixosModules is a bit easier)

There is a purity to Nix Flakes about how it includes system as an input but I wonder if that one papercut could have been fixed in the traditional way.

The boilerplate to creating a flakes is high.
I’m also trying to remember when I want to wrap things for each system or not

2 Likes

If some inputs expects a specific version of nixpkgs different from your own, that bloat means it keeps working. If you have more than one input using different versions of nixpkgs, you need both their nixpkgs to avoid breakage.

1 Like