4 ms·
> 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
by 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.
- spease 3y agoRegarding your last point, I actually do think that if the syntax to nix were different that it would make it much easier to understand. Though, the fact that it is clearly distinct from, say, Python or JavaScript gives it its own distinct feel as to how 'rigid' it is. To me the big stumbling block in the language is that it at first appears declarative, like you're writing a data structure that describes your configuration. However, when you start modifying anything about packages, you need to call functions to make changes because you're actually writing a function that's transforming input. So, you're thinking about what you're trying to do in a declarative, descriptive sense and certain parts of what you're writing are structured as such; but then other parts are structured as a transformation. Eg you write out a list of packages, but then if you want to change one of those packages, you need to start calling functions. As I mentioned in the Python example below, that can wind up requiring calling `override`, `overrideAttrs`, `overridePythonAttrs`, etc. Consider this: { packageOverrides = pkgs: { python3 = pkgs.python3.override { packageOverrides = python-self: python-super: { twitch-python = python-super.twitch-python.overrideAttrs (attrs: { patches = (attrs.patches or []) ++ [ ./twitch-allow-no-token.patch ]; }); }; }; twitch-chat-downloader = pkgs.twitch-chat-downloader.overrideAttrs (attrs: { patches = (attrs.patches or []) ++ [ ./twitch-chat-downloader-no-oauth.patch ]; }); }; } Versus this: { packageOverrides: pkgs { python3: { twitch-python: pkgs.python3.twitch-python { patches: pkgs.python3.twitch-python.patches or ./twitch-allow-no-token.patch } }, twitch-chat-downloader: pkgs.twitch-chat-downloader { patches: pkgs.twitch-chat-downloader.patches or ./twitch-chat-downloader-no-oauth.patch } } The latter is less "functionally perfect", but there is vastly less cognitive overhead required to lay it out because you are basically just describing your goal, rather than having to keep your goal in mind while implementing it functionally (and keeping exactly how nixpkgs is implemented in mind to correctly use those overrides). This is just off-the-cuff of what I intuitively expected when I first started using Nix, but it's more what I'd expect of a no-holds-barred purpose-built language for declarative package management. All the override stuff seems like needlessly noisy syntax; you know it's going to be everywhere, you might as well build it into the language. And it can probably be made even simpler with effort.
- emptysongglass 3y agoThat's literally why we make tools, so yes. Claims of Nix is too hard are like the BTRFS is unstable camp (which thankfully has mostly died out): they're holdovers from when these things were true. You have an entire industry building on top of Nix. Nix itself is complex because it has to be. Its DSL didn't need to be so terrible but the ideas Nix packages are not easy. So use the layer above the thing that is complex to make that thing easy. That's what tools are for.
- spease 3y ago> Claims of Nix is too hard are like the BTRFS is unstable camp (which thankfully has mostly died out): they're holdovers from when these things were true. This is a grossly misleading comparison. Or at least, it's maybe not the comparison you want to make. btrfs being unstable persists for a couple of reasons: - Even casual users have pretty much zero tolerance for a filesystem that self-destructs. Especially when the filesystem integrates RAID and snapshot functionality, so self-destructing takes backup infrastructure with it. - There are features that are legitimately unstable (RAID5/6), have unexpected side effects (defragmenting breaking reflinks), and seem unfinished (dedup requires external tools) Nix being too hard to use comes from, well, the stuff I mentioned, which is all from the last few years. Or, the laptop I installed NixOS on just this year, where the install finished, and then the user is left without any guidance how to set up their system. For me, that's OK; I can fire up a terminal and run nano then rebuild the system. But for any first-time user, I'd expect them to be SOL and stuck spending a Linux evening digging through the Nix manuals to figure out how to configure their system. (I just checked, and "First steps with Nix" starts with "Ad hoc shell environments", not anything to do with `/etc/nixos/configuration.nix`) If they're a DevOps user who's going to be using it for every day of their job, that's probably time well spent; but if they're a casual user who only wants to maintain the same set of software and settings whenever they get a new computer, it may just be more time-efficient to go through some pain every several years. For even a power user, they probably need to be working with a lot of software projects with native dependencies, or switching between computers a lot. Or just specifically curious about Nix. But in any case, I very much disagree that it's just an out-of-date reputation that's holding Nix back. Its functionality is really, really useful and widespread adoption would make language-specific tools somewhat redundant (eg virtualenv). I'm pretty sure the main thing holding it back is that it's just too hard to use and people silently abandon it.
- lloeki 3y ago> This is a pretty extravagant claim If one wants to just replace Homebrew it's really straightforward: Install: /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" sh <(curl -L https://nixos.org/nix/install) --daemon Usage: brew update nix channel --update brew list nix-env --query --installed brew search ruby nix-env --query --available --status ruby (or https://search.nixos.org/packages) brew install ruby@3.2 nix-env --install -A nixpkgs.ruby_3_2 brew upgrade nix-env --upgrade brew uninstall ruby nix-env --uninstall ruby I really don't see how it is "unacceptably bad". You don't even have to understand anything about Nix and still be able to use the damn thing. Yes the real-version vs attribute-name-that-contains-a-version is a bit wonky but in practice that's seriously not an issue. But really, for versions you can actually pick whatever version you wish by hitting a specific channel state: nix-env -f https://github.com/NixOS/nixpkgs/archive/nixos-23.05.tar.gz -iA ruby nix-env -f https://github.com/NixOS/nixpkgs/archive/88f63d51109.tar.gz -iA ruby ... which is a) completely generalisable for anything even those packages that don't have versions and b) completely removes any problems with dependency version conflicts. --- (Screw naysayers that would tell you not to use nix-env, because that is just not productive to turn people away. In practice nix-env works, as in, it pragmatically solves the real-world above problem for people, who can later decide on their own if they want to stop there or move to "better" things should they feel any limitation with that or another use case; and at that stage they'll be more comfortable doing so than when they just start using the thing. Best way to climb a mountain is one step at a time.) --- And then from there you get the other benefit of being immediately able to ramp up to having a per-project `shell.nix` and just be done with it by typing `nix-shell`: { pkgs ? import <nixpkgs> {}, }: pkgs.mkShell { buildInputs = [ pkgs.ruby_3_2; ]; } Or if like me you want to get fancy†: { pkgs ? import <nixpkgs> {}, }: let # because I like it this way and can later reference ${ruby} ruby = pkgs.ruby_3_2; whatevs = pkgs.whatevs42; in pkgs.mkShell { buildInputs = [ ruby whatevs pkgs.foobar ]; # this is not nix-related, I just find that convenient 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" ''; } Replace with this if you want to pin to a specific point in time: pkgs ? import(fetchTarball("https://github.com/NixOS/nixpkgs/archive/31b322916ae1.tar.gz")) {}, You can even have multiple of those: pkgsA ? import(fetchTarball("https://github.com/NixOS/nixpkgs/archive/31b322916ae1.tar.gz")) {}, pkgsB ? import(fetchTarball("https://github.com/NixOS/nixpkgs/archive/88f63d51109.tar.gz")) {}, ... buildInputs = [ pkgsA.foo pkgsB.bar ]; Bonus: it also Just Works on your favorite Linux distro. In under 5min it can be immediately useful to anyone who knows how to use a package manager (may it brew or pacman or apt) without having to dive on any Nix detail. I will not buy that nixlang is a barrier in that case, you don't have to know nixlang to understand what is happening here. † Actually I just realised that I could probably use ${ruby} - which stringifies to the installed path on disk - and do: export RUBY_VERSION="$(${ruby}/bin/ruby -e 'puts RUBY_VERSION.gsub(/\d+$/, "0")')" to reference the actual path, or maybe even just ${ruby.version} or something and not even subshell capture. Not that it matters.
- hollandheese 3y ago> now a large quantity of tooling that makes it as easy to work with as Homebrew. Now, that's a pretty extravagant claim. Homebrew can be used by basically anyone. It took me several attempts in the past few days to even get Home Manager installed with Nix on Fedora Silverblue because the Home Manager 23.05 channel package was broken and I had to use the master channel package to get it to work.
- spease 3y ago> large quantity of tooling Please no. I want one tool that works well, not N tools each with their own idiomatic way of doing things that everybody has to install and learn. Looking over the install guide, this looks like it's just as bad as nix, it just hasn't been around as long. There are three approaches to installing nix that are suggested (the vanilla route, Determinate, and "this script") that are left up to the presumably new-to-nix user to research which one to use. Then it references flakes, as if you're expected to understand them, and links to the main nix article with dozens of pages. Then if you used the first or third approaches to install nix (but not the second), you need to run a couple shell commands. Then you need to run a nix command. Then edit a text file. Then set your "bling" level, which just isn't explained anywhere. Then another nix command. Then another two fleek commands, which aren't even provided, even though they're the first two fleek commands the user will ever issue. And then, finally, you've got fleek installed. I think. It could use a "Congratulations!" or some party emojis to let you know you're done, rather than immediately jumping into the deprecated install options (and why are these even here if they're deprecated? How am I as a complete n00b user supposed to make the judgment call that my need to use them outweighs them being deprecated?). Users that are comfortable with the level of shell involvement required to install Arch may find it familiar, but I would not expect someone accustomed to primarily using a macOS UI to find it reasonable. And this appears to mean you can manage packages (but not the version of them, nor the version of nix, so you've lost reproducibility), your path, and "bling". But presumably, not `shell.nix`. And I'm guessing anything more advanced requires you to rewrite your .yml in Nix anyway. So it's a lot of work to ask a first-time user to do, advanced users will find it of limited usefulness, and even the install process makes it glaringly obvious that it's a very incomplete abstraction with a lot of holes. This also means that people with Nix knowledge will be maintaining the tool and polishing its tools instead of Nix, so only a subset of downstream users will gain from any improvements. Essentially: https://xkcd.com/927/ https://xkcd.com/927/. To be fair, I realize it's not a zero-sum game, and it's probably a lot easier and more rewarding to contribute to an independent project. Sorry for the harshness of the reply, I realize a lot of work went into fleek. My frustration comes from a place of repeatedly losing a lot of time to tools that people think are user-friendly because they mentally excuse all the work they're offloading onto the end-user as justified. The fact of the matter is that when I reach for a tool, more often than not I want to immediately use that tool, then put it down as quickly as possible and get back to focusing on what I was doing. I don't want to execute a half-dozen commands or have to make uninformed decisions just to get started. This is why Nix itself is so frustrating to me; the end result is indeed as promised and reproducible, but getting there often involves so many edge cases, gotchas, exceptions to the rule, or simply flat-out broken stuff that it completely derails my focus from whatever I was trying to do. I think (though perhaps its my own bias) most users are the same for any popular tool. There are some niche users that use it every day or all the time, but in the case of a package manager like nix, I probably only interact with it briefly when I need to install a new program, change dependencies for a software package, and so forth. So, a few seconds every few days or weeks. Even as a developer.