Pure-eval by default, without committing to flakes

I’m a smidge frustrated and am desperately trying to be constructive →

The number one most important features of flakes, for me is that my code is actually portable, actually reproducible, and I can actually hope that a random Nix repo will be easily consumable, rather than seeing niv and knowing that I’m probably going to have to fork and patch out impurities.

So, is there some way that we could switch to pure eval by default, without committing to the rest of flakes? While the CLI seems like a huge obvious improvement that makes nix more obviously valuable and discoverable to users… it’s just more fluff. The lock mechanism, fluff (because of flakes-compat). But the guarantees of pure-eval? Very real.

(EDIT: how did I write this entire post without mentioning NIX_PATH? Maybe that’s for the best.)

2 Likes

AFIAK, you would need to something like:

let
  pinnedNix = import (fetchTarball ... );
  nixpkgs = import pinnedNix { config = {}, overlays = []; };
in nixpkgs.package;

nix will still “prefer” items which are explicitly set. You can replace the pinnedNix logic with niv if preferred, niv overlaps greatly with the locking aspects of flakes.

(EDIT: how did I write this entire post without mentioning NIX_PATH ? Maybe that’s for the best.)

NIX_PATH is only relevant if you do something like <nixpkgs>. (which is still very common in shell.nix’s)

Huh? I know the implications of pure-eval and had my own pure-eval-compatible pinning system before flakes (turns out, flakes is easier).

And yes, NIX_PATH and <nixpkgs> is one example of such impurities that are often widely used in non-flakes Nix projects and thus limit their external usability.

The important point is: pure-eval by default.

(EDIT: I’m actually specifically sympathetic to the complaints that flakes could have been built out of tree, considering I’d basically built my own tooling to track branches and lock commits, and then removed it in favor of flakes. But even if I build 100%-flake-compatible tooling, right now, it’s basically useless to me in the context of what I perceive to be a major problem for Nix right now, because I get no pure-eval guarantees.)

2 Likes

Bump. Can we please have a conversation about this? Or can someone correct me? If we can’t agree on this then I feel like I’m wasting time continuing to invest here - its been years of this now.

I know at least @zimbatm wanted this.

I also would love an eval mode that is the same as in flakes, but that would require probably some argument passing to top-level file like { system, ... }: ...

1 Like

@colemickens what outcome of the discussion are you looking for?

The Nix project seems fairly heavily invested in Flakes so I don’t know if it could be changed.

Creating a little nix wrapper that calls --pure-eval --argstr system "$(arch)-$( uname | tr '[:upper:]' '[:lower:]') would be a great way to experiment.

Creating a little nix wrapper that calls --pure-eval --argstr system "$(arch)-$( uname | tr '[:upper:]' '[:lower:]') would be a great way to experiment.

So that my point is crystal clear, this does not help. This does not change the fact, that without pure-eval by default, I still have to go fork every last nix project to rip out their impurities to the top so that that wrapper would even work.

(pure-eval doesn’t even work on unstable nix, still, too, so uh again yay portability nix-build --pure-eval doesn't work (as I recall?) · Issue #4651 · NixOS/nix · GitHub).

The Nix project seems fairly heavily invested in Flakes so I don’t know if it could be changed.

Also to clarify, I’m not asking for any sort of change to flakes. I just want pure-eval by default, ahead of flakes landing. Because despite “heavy investment”, the future of flakes IS NOT CLEAR TO THE COMMUNITY. Neither process, or timeline. Literally, I do not understand the process by which flakes is expected to stabilize (will it RFC), nor do I have any reason to believe I’m closer time-wise to having that in stable nix than I was two years ago.

@colemickens what outcome of the discussion are you looking for?

I want to get people excited about committing to pure-eval by default, because I want the ecosystem to move forward. I want to advance my advocacy of Nix beyond this hold-my-breath-and-wait-for-it place that I’ve been in for the past two years. Pure-eval is the first pre-req to allow the nix ecosystem to expand to its more full potential. And yet it’s bundled with a number of other things that aren’t finished or are controversial in the community (for reasons that I understand and acknowledge).

I am not interested in evangelizing stable nix, and yet I feel increasingly awkward about advocating flakes. The fact that some users think an experimental feature is more appropriate for beginners than otherwise, shows where Nix’s usability is at today. The existence of that state at all, is confusing to outsiders and newcomers - and boy do I sympathize.

Committing to pure-eval (even via an RFC process) is a (more) achievable, small(er), still significant thing that lets us side-step countless issues regarding RFC process, community, UX changes, alt designs, etc. At which point in time experimentation with a wrapper would be perfect.

6 Likes

That sounds like a good idea, and I think is achievable. It requires some effort, mainly to draw a line from the current nix to pure-nix. Just like we had when we transitioned from unsandboxed to sandboxed builds. Most of the mechanisms for pure nix are in place in nix 2.4.

I think what’s missing is a convention that system is a magic variable that gets auto-injected with the current system unless specified otherwise (this would require to send a PR to Nix). Then you can have a default.nix that looks like this and is both backward and forward-compatible:

{
# `builtins.currentSystem` is not available in pure mode
# but it would work if system is injected
 system ? builtins.currentSystem
}:
let
  inputs = import nix/sources.nix; # some sort of pinning solution
  nixpkgs = import inputs.nixpkgs { inherit system; };
in
...

So then when you use nix-build classic, it picks the system from builtins.currentSystem, and with nix-build --option pure-eval true, it would inject the system as a default attribute. And if you want to build another system, you should still be able to override using --system <other system>.

WRT the future of Flakes, this is out of my control so I can’t really speak to that.

3 Likes