Hacker News new | past | comments | ask | show | jobs | submit login
Nix Language Primer (binaryphile.com)
122 points by binaryphile 8 months ago | hide | past | web | favorite | 64 comments



Does anyone know the rationale for creating Nixlang? Guix's use of Scheme proves there isn't a novel feature unavailable elsewhere, so it seems like a lot of wasted effort to implement a language that will likely only ever be used for one suite of programs. (And tooling; though almost none exists now, making the choice even more expensive.) I've tried to find one, but "nix" is a difficult thing to google for given the couple decades people have used it as a catchall term for unix and unix-like operating systems.


The Nix thesis may give some insight: https://nixos.org/~eelco/pubs/phd-thesis.pdf

Guix's use of Guile is fundamentally equivalent to the Nix package manager's use of the Nix language, but the approach Guix takes to organizing packages is very different. IMO, the Nix language approach is cleaner and more elegant when it comes to describing packages, but it's not clear whether that's worth the cost of using a domain-specific language. So, it still remains to be seen what's the best approach.


In Guix there are first class package objects that have references to other first class packages. Together they form a lazily evaluated graph. At a lower level, package objects can be compiled into one or many derivations, which is where things start to look more similar to Nix again.

In Nix the idea of "functional package management" is more visible as there are no packages but functions with arguments that would result in a package once evaluated.


Yep, Guix package objects with references to other package objects, versus Nix functions with arguments, is what I was referring to.


I think one of the keys features is lazy evaluation. Nixpkgs is one giant expression that evaluates to the complete set of packages that it provides. But since only some the attributes of that set are evaluated in any given invocation, it's still efficient. That can be vertical (eg, a package and it's dependencies) or horizontal (eg, the names of all the packages).

Guix shows that this isn't the only way to do it, but it is a good way.


Guix packages are also evaluated lazily. Package objects (values of the `<package>` type) can have an arbitrary number of declared inputs, which are package objects as well. These are not evaluated eagerly, of course.

Lazy evaluation does not have to be a language feature. In the case of Guix's `<package>` record, only some of the fields are delayed or thunked.


The difference is that the lazy evaluation needs to be baked in. There are many scenarios in the nixpkgs code-base where lazy evaluation is used on a not-package level. For example the NixOS module system depends heavily on it.

I haven't used Guix to be able to compare but it saved my life many times. I suspect the advantage for Guix is that it forces things to be better structured.


I like Scheme, but I think a lot of people actually wouldn’t want to write their packages and OS configuration files as S-expressions.

Nix is an extremely simple language with quite familiar syntax, a kind of JSON with functions and string interpolation.

Note also that Guix uses Scheme a lot more deeply than Nix uses the Nix language, in the sense that Nix uses e.g. shell scripts where Guix uses Scheme statements. Actual “coding” in the Nix language is relatively rare.


For me personally, as a person who's tried Nix, it's helpful that the Nix language is simple, self contained and easy to learn & grok quickly. I never learnt Scheme, and its use in Guix, though extremely interesting and appealing in theory, also feels notably overwhelming to me. That I'd have to learn a whole (presumably huge) R7RS or something, just to be able to use Guix. And then still have to learn the Guix "API" or DSL over that to be able to actually use it. While the Nix language is small and fully described in the Nix manual, in surprisingly few words.


Disclaimer: as a Guix co-maintainer I'm totally biased.

We don't use R7RS in Guix. You need to know about the Scheme syntax, obviously (including keyword arguments), and a couple of common procedures like `string-append`, but aside from that you don't really need to know much about Scheme at all.

What comes in handy is the Guix DSL, which provides a convenient way to specify packages and download origins. Guix also has a bunch of utility procedures that are useful extensions to their Scheme counterparts, such as `mkdir-p` (which does what you think it does) or the `substitute*` form to substitute expressions in a file or list of files.

One important difference between Nix and Guix is that Guix does not glue shell snippets together, but eventually compiles to Guile builder scripts, so it's Scheme all the way down.


Does Guix let you write interactive programs in Scheme as part of the system configuration? For example, defining a systemd service that uses Guile libraries and so on, as a subexpression of your OS configuration file? While also referring to shared variables like the system's hostname, etc?

I skimmed the paper on "Code Staging in Guix" and I think this should be very doable, but I haven't yet tried Guix for real.

This ability seems like it would have huge implications for system development... I've dabbled with such experiments using Nix, but the lack of hygienic code staging makes it a bit icky.


I don't know about interactive programs; in Guix we like system configuration to be non-interactive.

We don't use systemd but the Shepherd, which is written in Guile Scheme, so system services are indeed written in Scheme and can use Guile libraries. It is a common pattern to define services as the result of procedures taking variables as arguments.

System services in Guix don't have to be limited to Shepherd services. The system service framework in Guix is very flexible and can be used for almost any system setup task, even for mere "activation services" that create directories or files. Services can extend other services:

http://www.gnu.org/software/guix/manual/en/html_node/Service...


"familiar syntax"? Perhaps if you use Haskell or ML. That's probably a tie with s-expressions for popular syntax, but definitely not familiar to most people.

I really like nix the package manager and like NixOS the distribution, but learning nix the language was an awful lot of work, and I'm not convinced that it couldn't have been accomplished with a language that is more familiar (Guix demonstrates that baked-in lazy evaluation is not necessary). Even tiny things like "sets" as a name for an associative data type make it just that much harder to learn.


I haven't used nix, but this primer seems pretty straightforward. Was the learning curve you faced perhaps due to missing or hard-to-find documentation at the time, or something else?


I can relate to the earlier poster because I had the same problem finding all of these rules in one place. I wrote it to be precisely what I wish someone had told me before I got started.

Thank you for the compliment btw.


Yes, this primer would have been very helpful at the time. It's not like it's impossible to learn, but "familiar" it is not.


Do yourself a favor and watch Gabriel Gonzalez's talk "Nix: Under the Hood" ( https://youtu.be/GMQPzv3Sx58 ).


Nobody wants an arbitrary complex program for describing some trivial build steps. A programming language here is an anti-feature.

Ideal would be some DSL that isn't even Turing complete, but that's not practical at the moment. Maybe we'll get there some day.


It's difficult to not be Turing complete.

Makefiles are Turing complete: https://nullprogram.com/blog/2016/04/30/


Dhall and Dhall-nix are definitely a step towards that.


The ability to build abstractions is actually one of the key points of what makes Nix work, conceptually. So yes, a 'programming language' (how are you defining that anyway?) is absolutely a feature here.


Really what no one wants is a build step that takes forever. I'm more upset about how long nix-env -i takes than what the computational complexity of the programming language is


nix-env -i is slow because it has to evaluate every package definition. nix-env -iA is way faster.


I believe the motivation is that it eschews a lot of convenient crutches that would defeat the purpose of using nix (e.g. impurity) and adds a lot of convenient crutches that don't (e.g. a lot of implicit coercion for derivations).


Purity is a property of the functional methodology, not of the language. You don't need to use an effect-free language when operating in an environment that renders most effects void.


This is the first I've heard of Nix. A quick search reminded me that it has nothing to do with Nim, and turned up NixOS that I have heard of.

Here's a good description of why I should care: https://yakking.branchable.com/posts/what-and-why-nix/


A few correction on the otherwise good article:

* Nix 2.0 includes the repl. Just run `nix repl`

* The URL literal returns a string

* It would be good to explain how the `<...>` notation resolves paths from the NIX_PATH. For example `<nixpkgs>` looks for the `nixpkgs` key in `NIX_PATH=nixpkgs=/path/to/channel`


Thanks for the tips. We're still running on 1.11 because of problems with the recent glibc updates not matching the kernels in our deployment environment.


Finally got around to updating the post with your suggestions. Thanks again.


I switched all of my machines to NixOS for a few months. It was a wonderful experience being able to keep all my environments in sync. Nix the language is one reason I reverted to Ubuntu a few weeks ago. It just didn't mesh well with me....so when the inevitable problem occurred, it was a pain to deal with.

Other pain points:

- the NixOS learning curve is steep. There are two many tools with too many obscure options with sometimes overlapping features.

- small group of maintainers with no real documented security process...just an assurance that they path as quickly as possible

- many packages are old if they're not mainstream

I would love to see the concept of Nix reimplemented in a non-niche language without the fsf zealotry... I'd switch my company to something like this in a heartbeat. I think there's a real business opportunity for someone willing to take that on.


Imagine if all the right ideas from nix were taken out and combined with supervision trees, we would have super reliable distros and PaaS style one click infrastructure.


I don't understand why any languages exist for describing packages, its a layer above describing real dependencies like with make, ninja, etc. Just have an os written in something like premake but with a nicer DSL.


It allows to have some guarantees around the evaluation. The Nix expression language enforces things like not allowing arbitrary network access during evaluation. This is hard to do in a general-purpose language where network and file access are usually backed in.


This is easily done by chain-loading and system()


Make & ninja don't support two key activities:

- Versioning

- Getting the package (the actual URL, deciding if you want the package, determining which variant -- sometimes two packages solve the same dependency, etc.)


There is also https://jsonnet.org/


Yep they're pretty similar in goals, (and syntax?) except for that nix has built in support for derivations as a language feature. Nix is a language for derivations first a language for configurations second. Jsonnet is only a configuration language.


Why the need for another new language? Why not just use Guile or Scheme instead?


Probably the same reason why Guile or Scheme didn't get as wide-spread as other languages in general.


And what would that be? Because it is lispy? Or because of the restrictive GNU licenses?


Because nobody want to program in (((( Guile )))), to the point that even GDB disable its support as extension language.


However undesirable it appears at first, it does prevent the creation of an infinite number of new, incompatible languages. Config languages tend toward turing completeness and just become turing tarpits. That's why I'm asking about using a Lisp which is extendable to its problem domain.

Source on gdb disabling it? Just writing `gr` in gdb enters the guile repl.


It has been disable by default and is not even compiled-in anymore on all major linux distributions ( RHEL, Debian, ubuntu ).

>(gdb) gr Guile scripting is not supported in this copy of GDB.


What's your version? I'm on 8.2


I've had the displeasure of being forced to use nix and to me it seems like an overengineered piece of garbage. I don't see why anyone should use this vs competitors


Huh. I think this is the first bad experience with NIX I've ever heard on HN. Having only ever heard about it in passing but never worked with it myself I'm curious, what were you building when you'd encountered it and what issues did you have. Was there a particular language you'd preferred to have worked in?


> Huh. I think this is the first bad experience with NIX I've ever heard on HN.

Strictly speaking "Nix" may refer to two things, and I'm not sure which thing either of you are referring to, nor if you two are referring to the same thing.

Personally I found the nix package-manager a bit odd, but to me that was not a deal-breaker for trying out NixOS. The bigeset problem for me was lacking packages... I was however willing to help the community out by trying to package the things I needed which wasn't already provided for.

But then I had to use the Nix language. And that was a great displeasure, and I ended giving up NixOS all together because of this one pain point.

And to address your comment: I think I've seen similar comments on HN at regular intervals. It's definitely not a one-off comment.

If they had used another more standard language (like LISP!) I would probably still be on NixOS as my main OS.

In the long run, I really hope Guix manages to get something good going. My biggest concern for Guix is that they will restrict the available packages by being overly zealous about package-licensing (like everything the FSF does).


But then I had to use the Nix language. And that was a great displeasure, and I ended giving up NixOS all together because of this one pain point.

Could you articulate why? I have only been using Nix for ~3 months, but so far I do not really have qualms with the Nix language. Maybe it is because I have written a fair bit of Haskell before.

(Of course, I understand the argument that it would have been more comfortable if an existing language like Scheme was used.)

My biggest concern for Guix is that they will restrict the available packages by being overly zealous a bout package-licensing (like everything the FSF does).

Same here. When I got interested in NixOS/GuixSD I seriously considered using GuixSD, because Scheme is more familiar. But there are just some proprietary packages that I need for work (Skype) and at home. I didn't really feel like having to rely on third-party repositories, etc.


> Could you articulate why? I have only been using Nix for ~3 months, but so far I do not really have qualms with the Nix language.

I mean... It's not a "real" language in the sense that there exists any tooling, debuggers, unit-test suites or anything.

You write your recipes and try to see if they work. And when they don't you have no idea why.

With a real programming language (like Scheme) I could evaluate something working, compare it to my non-working stuff and probably figure out where it all went wrong. Chances are I could probably expand the macros and debug my way to a solution right within Emacs without ever launching a single external tool.

There's just this huge gap between what you get and what you expect. And with Nix being a one-tricky pony, there's almost zero incentives for the community at large to try bridge that gap.


I found the nix-repl to be very helpful in this situation...you can also invoke command-line expressions directly with nix but it's a bit more arcane.


> In the long run, I really hope Guix manages to get something good going. My biggest concern for Guix is that they will restrict the available packages by being overly zealous about package-licensing (like everything the FSF does).

Guix co-maintainer here. The Guix project is not run by the FSF; it is part of the GNU project, because we subscribe to the idea of giving users the tools to take control of their computing.

Packages available through the official Guix channel must be compliant with the Free System Distribution Guidelines:

    https://www.gnu.org/distros/free-system-distribution-guidelines.html
The large package collection that comes with Guix is trivially extended through channels: a git repository containing Guix modules defining extra packages. You can do without git and just add a local directory of modules to GUIX_PACKAGE_PATH.

At the institute where I work we're using channels to provide packages that are not available in Guix proper. It's very simple.


I recently posted on HN complaining about Nix and Guix.

For myself (and I suspect many people) when installing GuixSD, before I even have the system running, I'm going to need to figure out how to package the Linux kernel with firmware for my wifi. Otherwise my ~$3k laptop is basically a brick. That is a big ask right up front. Glancing at the mailing list it seems like discussing non-free software is discouraged. Even if there is some well maintained Linux package on Github it would be difficult to find and evaluate before having any experience with Guix.

It is your project and you can obviously have whatever rules you want. It just seems that Guix and Nix made some odd choices that are going to keep both projects in a very small niche.


Would you mind elaborating? Who would you say are the competitors? I completely agree it's a waste to over complicate things if you never leverage it, but Nix seems like the perfect solution to a problem I can't imagine I'm the only one to have. Being able to build against different versions of glibc and a whole stack of dependencies out of the box is kind of amazing after dealing with dependencies. Flipping between version stacks by flipping a symlink is insane.


Why would anyone use it versus its competitors? Here's why:

1. You can uninstall a thing and it will no longer affect your computer.

2. Your environment will work the same way on your colleague's computer.

3. Your environment will work the same way years down the line.

4. Your environment will probably work the same way years down the line, even after you've applied security updates. If it doesn't, it will probably be clear what's broken/different.

5. You can install many different environments on the same machine, and they will probably all work. On the rare occasions where they don't, it will be because they interact with system services or limited system resources, and it will be clearly defined how that happens.

6. All this is true across most programming languages, system services, and all sorts of tools.

7. You can combine this with direnv (or direnv-mode for Emacs) to make it so that you can just cd into a directory and magically get all the dependencies.

8. You can put the recipe for your environment in Git.

Other than Nix and Guix, I don't know of any tool where these things are true. Docker and Virtual Machines get sort of half way, but tend to rot over time.

But yes, it's difficult to learn and in many cases the usability is poor.

Final bonus point:

9. Nix is so good that it can even make Autotools work correctly.


I don't care about those features, I just wanted to download the packages and it's dependencies and in the end it never did that.

Nix is probably good for many things, but it's not simple and to me it's trying to do too many things.


The reason you can "download a package and its dependencies" with other package management systems, is not because they are technically superior, but because they've had countless man-hours of contributors making sure that their central repository contains packages which work well together.

As soon as any assumption changes, like for example, adding a third-party application repository, then you need to concern yourself with all of the issues mentioned above, or you'll end up wasting your days on manually building and installing packages locally.

The point of Nix is to do this once only. If you build and it runs, it should do the same for everyone, regardless of what other repositories they use. Package management becomes completely modular and does not depend on some centralized state like a central software repository, or the state of your machine at time of install.

Nix isn't trying to do too many things. It's actually much, much simpler than using a bunch of semi-compatible tools which all attempt to do similar things in ad-hoc ways. Nix is just a dependency management system where dependencies are specified by their content hash. Your machine contains a content-addressable store of objects which can be precisely referenced by any other object. It takes the guesswork out of dependency management. Other dependency managers effectively run a search engine to try and find an appropriate dependency from some search terms.


That is not what "overengineered garbage" means.


I'm using Nix to download packages and its dependencies to a cluster of 250 nodes around 10 times per week. And it always does that.


Overengineered, hah. Most UX issues could be chalked up to it not doing enough!


Exactly, let's make the package manager do 100000 things.


How were you forced to use Nix? I didn't know anyone was using Nix in a professional environment.


Cardano the cryptocurrency is written in Haskell and uses nix as it's package manager


I for one did use it professionally for years.


Please elaborate.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: