5 ms·
Nix is great in theory, but the user experience is unacceptably bad, especially for anyone who isn’t a software engineer. While it does do an excellent job of
by spease 3y ago
Nix is great in theory, but the user experience is unacceptably bad, especially for anyone who isn’t a software engineer.
While it does do an excellent job of reproducibility if the inputs are the same, I’ve found it to be prone to be breaking if you switch to newer versions.
Part of the reason that it’s painful to use is because while it’s marketed as “declarative”, in actuality it’s functional, which results in a lot of convoluted syntax to modify parameters for a package, which varies based on the language of the package.
There seems to be some awareness of the usability issues, but the changes have seemed both a step forward and backwards. For instance, you used to be able to use the “nix search” command to look up package names; now it’s gated behind some arcane syntax because it’s “experimental” and something to do with flakes. And flakes seems like it has the consequence of fragmenting the package repositories and making it impractical to improve the language.
I still have helper functions to wrap desktop applications that I had to set aside, because some upstream changes broke it and neither I nor anyone on the nix forums could figure out if there was even a way to include them in the “darwin” namespace with an overlay. My goal was to make it as easy as homebrew to add an app to nix’s repository.
Another evening I sat down to make a minor feature to a Python library and decided to use a nix environment. In theory, this should have been better than a virtualenv. In practice, there’s no in-tree support for specifying specific versions of Python libraries, and mach-nix had trouble with the dependencies, so I wound up just filing a bug report and gave up on the feature I was trying to implement.
On the plus side, NixOS finally has a graphic installer, but I don’t think that helps macOS.
I’m still hopeful that the community will decide to prioritize usability, but after spending an aggregate of months of time trying to get things to work and continually running into time-consuming roadblocks, it’s not something I would recommend lightly to someone who wants to be more productive.
- emptysongglass 3y ago> Nix is great in theory, but the user experience is unacceptably bad, especially for anyone who isn’t a software engineer. This is a pretty extravagant claim. It was once very bad but there's now a large quantity of tooling that makes it as easy to work with as Homebrew. For managing their home, people can use Fleek, which makes Home Manager straightforward to work with: https://getfleek.dev/ https://getfleek.dev/
- ghusbands 3y agoIt feels like every time someone complains about Nix being hard to use and understand, there's a response that claims it's great and that you just have to use X or Y. Oddly, what X or Y are seem to differ greatly.
- lloeki 3y agoI don't buy the "just use X or Y" either. It's like "just use ohmyzsh". That's why I use dead simple nix stuff, which gets me 90% of the way (more like 140% if compared to Homebrew). If one's goal is to replace - and solve a few problems inherent to - homebrew or apt it's really not hard, see my sibling comment.
- emptysongglass 3y agoI disagree and that's why I recommended Fleek. What is dead simple to you about Nix is not dead simple to others. Especially given your other comment: it's filled with Nix-isms that most people used to imperative thinking would not grok.
- lloeki 3y ago> it's filled with Nix-isms Which part? For the commands, update install upgrade uninstall are straight the same. There's an additional -A on install, but you don't need to understand it. The commands are even handed over to you on https://search.nixos.org/packages https://search.nixos.org/packages. There's an additional nixpkgs prefix but it's perfectly understandable that it's coming from the channel name: # nix-channel --list nixpkgs https://nixos.org/channels/nixpkgs-23.05-darwin It's really not out of this world to mentally map this to homebrew taps or apt repos. nix-env --query is different from homebrew but its design is much the same as pacman -Q and people have no trouble with that (I won't even dive on apt-get vs dpkg vs apt-cache) nix-env -f <github archive url> --install nixos-23.05.tar.gz is such because it's the branch name, 88f63d51109.tar.gz is just the commit sha. These are GitHub things. Providing the source of packages as a --file option is hardly inscrutable nor even a nix-only thing. But if you really want a parallel with homebrew, then homebrew has taps, and nixpkgs has channels: nix-channel --add <url> name is directly equivalent to brew tap user/repo <url>, down to installing with brew install user/repo/package vs nix-env --install -A name.package. And there it is, you have your 1:1 nixism-free homebrew:nixpkgs replacement, and can manage your packages with nix just like you do with homebrew. > there's now a large quantity of tooling that makes it as easy to work with as Homebrew My point is that there is no tool that could "make it as easy to work with as homebrew" because homebrew:nixpkgs is already 1:1, and people confused by using nix this way would equally be confused by using homebrew. You mentioned Fleek, which seems like a nice tool, but managing home is outside the scope of homebrew so I don't see how it follows that it makes nix "as easy as homebrew". It apparently has some homebrew bits, but it's a third party tool to both homebrew and nix. Don't get me wrong, tools can be nice, I use nix-darwin myself, not too sure about going home-manager yet. --- Now that second part where I talk about shell.nix, maybe it's this one you took issue with? I aimed it at being a next step, not addressing the above point, but attempting to demonstrate the immediate value it can give without understanding the slightest bit of nix and especially eschewing all the nix-specific vernacular. The first time it is encountered, this is probably what a reasonable non-nixer vaguely interested in would see: { pkgs ? import <nixpkgs> {}, # ^^^^^^^^^^^^^^^^^^^^^^^^ # okay so I've seen nixpkgs before, that's my channel, where the packages come from # that `?` looks odd but well, whatever, there's a "pkgs" thing and an import of sorts. }: pkgs.mkShell { # ^^^^^^^^^^ # this is: # - a shell.nix file # - that has been described to me as being used by a `nix-shell` command # - and that thing is name mkShell # so probably it's going to tell things how to make that shell buildInputs = [ pkgs.ruby_3_2; # here's that pkgs that I've seen above # it has a dot plus a package name just like when I did nix-env --install # except it had nixpkgs before the dot there # oh, I see "pkgs ? import ..." sort of creates that variable or whatever from nixpkgs # so this is a list of packages from nixpkgs ]; # and so that make a shell thing uses that list to get its dependencies # and it's getting named buildInputs, whatever } # so that's it, it makes a shell using a list of packages from nixpkgs And proceed to add whatever package they see fit by looking them up on the search page. Understanding of nixisms doesn't matter. Second example: "let .. in" isn't a very special nix construct, "let" is extremely frequently used in a number of languages (including JS) to define variables. "whatever .. in" is a very common construct as well. It's quite obvious that the example hoists out a few elements by assigning variables between "let" and "in", possibly creating some form of scope after "in". It also introduces "shellHook", which literally contains a multiline bash string; again I feel like it's quite obvious that the "make shell" thingy is going to call that hook at some point. Last bits shows that the nixpkgs channel can be replaced with fetchTarball + a GitHub tarball archive URL, and that you can have multiple of these at once with different names, referencing packages from one or the other. > that most people used to imperative thinking would not grok. I can hear that the syntax is an oddball, but even then that example is really not that hard to wrap one's head around for anyone who has a passing familiarity with programming, whether functional or imperative. Doesn't mean that they would fully appreciate that { foo ? "bleh" }: whatevs = 42 is actually a function definition equivalent to JS function(foo = "bleh") { return { "whatevs": 42 }; } but that's immaterial to understanding what is happening here and even being able to add/remove packages, change versions, pin sources, or hack that shell bit and ultimately have it be pragmatically useful. So I don't think the nixisms are any problem in this case because they can be completely ignored. I'm also wondering if people have an epidermic reaction to the syntax, combined with the constant rumour that nix is hard and hardly usable does not set them up for success. I mean, would people be as hung up if it were written this way? args: pkgs: <nixpkgs> let: ruby: pkgs.ruby_3_2; whatevs: pkgs.whatevs42; in: pkgs.mkShell: buildInputs: - ruby - whatevs - pkgs.foobar shellHook: | export RUBY_VERSION="$(ruby -e 'puts RUBY_VERSION.gsub(/\d+$/, "0")')" export GEM_HOME="$(pwd)/vendor/bundle/ruby/$RUBY_VERSION" export BUNDLE_PATH="$(pwd)/vendor/bundle" export PATH="$GEM_HOME/bin:$PATH" or this way? function(pkgs = import(NixPath.resolve('nixpkgs'))) { let ruby = pkgs.ruby_3_2; let whatevs = pkgs.whatevs42; return pkgs.mkShell({ buildInputs: [ ruby, whatevs, pkgs.foobar, ], shellHook: ` export RUBY_VERSION="$(ruby -e 'puts RUBY_VERSION.gsub(/\d+$/, "0")')" export GEM_HOME="$(pwd)/vendor/bundle/ruby/$RUBY_VERSION" export BUNDLE_PATH="$(pwd)/vendor/bundle" export PATH="$GEM_HOME/bin:$PATH" ` }); } Because at the end of the day, it's not that different in complexity, from, say, a Gemfile, a PKGBUILD, or a homebrew formula.
- lloeki 3y ago> Part of the reason that it’s painful to use is because while it’s marketed as “declarative”, in actuality it’s functional You're correct, with a twist: NixOS is declarative, nix is not - it's indeed functional machinery. This exposes a declarative interface: https://github.com/NixOS/nixpkgs/tree/master/nixos/modules https://github.com/NixOS/nixos-hardware https://github.com/LnL7/nix-darwin/tree/master/modules This does not: https://github.com/NixOS/nixpkgs/tree/master/pkgs but being functional makes it easier for the declarative bits to exist, e.g the next step in this case (PR pending on my side to contribute just that upstream) is: - creating systemd.services."nqptp" with enabled = false as a default - transforming services.shairport-sync to reference systemd.services."nqptp".enabled = true when enableAirplay2 = true https://github.com/NixOS/nixpkgs/issues/258643 It also makes pinning/rollback to a specific version without touching the remainder of the system a spectacular non-event: https://github.com/NixOS/nixpkgs/issues/245769 Even when `.package` is not made available it's only slightly harder to use another module with disabledModules + import. > Another evening I sat down to make a minor feature to a Python library and decided to use a nix environment. In theory, this should have been better than a virtualenv. In practice, there’s no in-tree support for specifying specific versions of Python libraries, and mach-nix had trouble with the dependencies Maybe you tried too hard to "nixify" everything, including managing the whole of python stuff. That's what I use: # shell.nix { pkgs ? import <nixpkgs> {}, }: let # get these python packages from nix python_packages = python-packages: [ python-packages.pip ]; # use this pyton version, and include the above packages python = pkgs.python39.withPackages python_packages; in pkgs.mkShell { buildInputs = [ python ]; shellHook = '' # get python version export PYTHON_VERSION="$(python -c 'import platform; import re; print(re.sub(r"\.\d+$", "", platform.python_version()))')" # replicate virtualenv behaviour export PIP_PREFIX="$PWD/vendor/python/$PYTHON_VERSION/packages" export PYTHONPATH="$PIP_PREFIX/lib/python$PYTHON_VERSION/site-packages:$PYTHONPATH" unset SOURCE_DATE_EPOCH export PATH="$PIP_PREFIX/bin:$PATH" ''; } And then just `pip -r requirements` or whatever poetry you fancy. On a specific project I needed a bit more control, and some fix because of a braindead build system. Fix once and be done with it. # shell.nix { pinned ? import(fetchTarball("https://github.com/NixOS/nixpkgs/archive/88f63d51109.tar.gz")) {}, }: let # get these python packages from nix python_packages = python-packages: [ python-packages.pip ]; # use this pyton version, and include the above packages python = pinned.python39.withPackages python_packages; # control llvm/clang version (e.g for packages built from source) llvm = pinned.llvmPackages_12; in llvm.stdenv.mkDerivation { # unique project name for this environment derivation name = "whatevs.shell"; buildInputs = [ # version to use + default packages are declared above python # linters pinned.shellcheck # for scripts pinned.bash pinned.fswatch pinned.rsync # for c++ dependencies such as grpcio-tools llvm.libcxx.dev ]; shellHook = '' # get python version export PYTHON_VERSION="$(python -c 'import platform; import re; print(re.sub(r"\.\d+$", "", platform.python_version()))')" # replicate virtualenv behaviour export PIP_PREFIX="$PWD/vendor/python/$PYTHON_VERSION/packages" export PYTHONPATH="$PIP_PREFIX/lib/python$PYTHON_VERSION/site-packages:$PYTHONPATH" unset SOURCE_DATE_EPOCH export PATH="$PIP_PREFIX/bin:$PATH" # for grpcio-tools, which is building from source but doesn't pick up the proper include export CFLAGS="-I${llvm.libcxx.dev}/include/c++/v1" ''; } Sure that's not pure nix or flakesy or whatever, but simply delegating python things to python-land is a very pragmatic move, idealistic purity and reproducibility of everything be damned, it is instantly better than homebrew or docker because that setup gets you a consistent tooling environment on any Darwin (Intel or ARM, at whatever version) or Linux (whether it's NixOS or just nixpkgs). Also it's super amenable to collaborators who don't know the first thing about nix: they can blindly type `nix-shell` and be all the merrier, handling their python stuff as usual, and completely removing a whole class of "it works/breaks on my machine".