Nice write up, the use of the angle bracket syntax is interesting. Though i think i disagree that adding buitins.currentFlake
would be pure. It’s also very very hard to define, when it should switch a flake input of the toplevel flake. So I’m not sure such a chabge would be a net positive
This is quite interesting indeed, but why don’t you use self
instead of getFlake
?
flake.nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgslib.url = "github:nix-community/nixpkgs.lib";
};
description = "Trivial flake that returns a string for eval";
outputs =
{
nixpkgslib,
nixpkgs,
self,
}:
{
__findFile =
nixPath: name:
let
lib = nixpkgslib.lib;
in
lib.getAttrFromPath (lib.splitString "." name) self;
hello = "Hello from a flake!";
example = builtins.scopedImport self ./default.nix;
};
}
default.nix
<outputs.hello> + " and welcome to Nix!"
$ nix eval .#example
"Hello from a flake! and welcome to Nix!"
Thank you that’s great.
Originally I had the lookup in a different file before I discovered scopedImport
I’ll update the example with this
While I appreciate the concept of new angle bracket syntax, I don’t think the Nix language needs flake-specific syntax. Flakes have been adopted by the majority of the community - but NIX_PATH was previously adopted by the majority of the community. The shortcomings of flakes are well-documented, and they are still experimental for established reasons. Even the biggest proponents of Flake stabilization acknowledge that future iterations on Flakes will happen, and plan on supporting them.
I don’t think entrenching ourselves further in flakeland is the right move. I’d prefer to see angle brackets used for something more meaningful, like providing a module system with faster evaluation.
as always another cool article from @fzakaria
personally i prefer a simplified __findFile
like this:
__findFile = nixPath: name: inputs.${name};
that is… map each flake input, but not output
my reasoning is that this is a more natural progression from channels to flakes and keeps the same semantics - flake inputs are an extension/improvement/natural progression of channels
but then again… i don’t like most flake outputs, so i guess i have a bias there…
Thanks for the kind words.
The problem with ${name} is that if you do “a.b” it puts it as a single attribute quoted. I guess for inputs it may not matter.
Why support output?
Well I want to access my own packages or modules sometimes instead of through an overlay
I just pushed an update to the blog with your improvement – thank you.
The ability to use angle brackets is very interesting, though scopedImport
has huge performance penalty (iirc it disables memoisation). Some testing with nix-instantiate
(I don’t use flake but npins) shows that the evaluation time goes from 17 seconds to almost 30 seconds, doubling the evaluation time…
might be slighlty off topic:
eventual deprecation of
NIX_PATH
?
that document was presented forever ago at a nixcon for ideas on where to head… it is vaporware now, not going to happen
as far as deprecating NIX_PATH
though… i bet some people actually want to do that - i hope that won’t happen
recently i started playing around with lon ( @nikstur) and started utilizing angle brackets again after having switched to flakes many years ago now
one of my shell.nix
files looks like as an experiment:
let
sources = import ./lon.nix;
pkgs = import sources.nixpkgs { };
in
pkgs.mkShell {
shellHook = ''
export NIX_PATH=${pkgs.lib.concatMapAttrsStringSep ":" (k: v: "${k}=${v}") sources}
'';
}
i hooked that up to direnv
and now whenever i enter that project directory i can access <nixpkgs>
in a well defined way - whether in repl
or something like nixos-rebuild
, everything has access to NIX_PATH
i haven’t been using this long enough to decide how much i like it yet… but it has been a blast from the past at least
maybe explains why i only encountered it a couple hours ago
I really think NIX_PATH is great. It supremely useful when mucking about with the repl to just say “gimme the nixpkgs in context” whether that’s system wide, or in some project, it’s useful shorthand. I use it all the time for answering “what about??” repl questions in the global system context. If I want to turn into something stable/shareable, well I can just copy the system nixpkgs pin and move from ephemeral to permanent.
NIX_PATH <3 for ever!
I think builtins.scopedImport self
is rather reckless. You could accidentally override a number of things. It would be safer to do something like this:
{
outputs =
{ self, nixpkgs, ... }:
let
inherit (nixpkgs) lib;
scope = {
__findFile = _: name: lib.getAttrFromPath (lib.splitString "." name) self;
};
in
{
scoped = builtins.scopedImport scope ./default.nix;
};
inputs = {
nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz";
};
}
If you were to define something like import
in the outputs of the previous example (not intending to override the import
global), you might just break all subsequent calls to import
. Obviously, I don’t think anyone would actually do that (at least, I hope not), but I’m sure you can see how similar things could happen:
# scoped.nix or selfed.nix
{
pkgs = import <inputs.nixpkgs> { };
}
# flake.nix
{
outputs =
{ self, nixpkgs, ... }:
let
inherit (nixpkgs) lib;
scope = {
__findFile = _: name: lib.getAttrFromPath (lib.splitString "." name) self;
};
in
{
inherit (scope) __findFile;
# Oh no! Unanticipated name conflict!
# If we use `self` as the scope, this overrides the `import` global.
import = import nixpkgs { };
# In `selfed.nix`, `import` is a set.
# That causes an error.
selfed = builtins.scopedImport self ./selfed.nix;
# In `scoped.nix`, `import` is the builtin function.
# Everything goes as expected.
scoped = builtins.scopedImport scope ./scoped.nix;
};
inputs = {
nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz";
};
}
It’s littering, at best, because everything in outputs
becomes global.