I spend a weekend every so often defining the core of what I want next time I upgrade, but just find it so annoying I'm sure I won't use anything I've written until there's a major change in the ecosystem.
I spend a weekend every so often defining the core of what I want next time I upgrade, but just find it so annoying I'm sure I won't use anything I've written until there's a major change in the ecosystem.
Putting aside the poor typing (the lack of proper typing is a shame, so valid criticism), I actually really like the language - it's genuinely a great DSL for the particular problems it's supposed to handle.
It does take a bit of use for it to click, though. A lot of it has to do not with Nixlang itself but about learning nixpkgs' idioms.
I think a good IDE integration could solve this, but not sure how much is possible.
That said, I strive to structure my nix source so that portions of it can easily be pasted into a repl. ReadTree goes a long way in that regard: https://github.com/tvlfyi/kit/tree/canon/readTree
More to your point, though: I think a lot is possible. Although nix is very dynamic, it is also, for all intents and purposes, side effect free. I've had this idea that a sufficiently advanced IDE should be able to evaluate your nix code and tell you exactly what the possible values (not just types, but value!) are for any particular variable.
Similarly to the REPL, I'm often using `nix-instantiate --eval -E 'somethingsomething'` so it should definitely be possible.
It has jump to definition and autocomplete. Which is very nice.
It's not perfect. But it's pretty good
But I think this also stems from the fact that the default state of nixos is "a general purpose linux system" and so instead of just starting at 0 and adding the things you need, you have to mix adding and removing things which IMO makes things much more complicated (except maybe for newbies to linux who don't know what's necessary for a running system).
With a default config you start with a console, systemd, dbus and some things to make it boot. There is barely anything.
NixOS is much better because you can inspect the changes after the fact. You also know which code to look for, which is a luxury. If the code seems too much, there's the repl to help. Changes are also much easier to revert.
`nix-instantiate --eval -A config.services.resolved.enabled /etc/nixos/configuration.nix`
This is way better than a stateful package manager making a non-revertible change without even telling me.
For "what symbols are available", the nil LSP implementation[1] works for anything in scope that doesn't require evaluation. It also includes completions for the stdlib and NixOS options (in certain contexts).
Another LSP implementation is nixd[2], which is trying to tackle the problem of evaluations for completion.
$ nix repl
nix-repl> :l <nixpkgs>
nix-repl> {press tab for auto-complete}
Unlike most languages, the symbols available are completely determined by the scope. Just look at the let expressions in effect. There's no magic.
As for nested expressions, that's a typing problem, which was already mentioned above as a pain point (although there are several efforts to fix this).
1. At the end of local variables
let
a = 1;
b = 2;
in
a + b
result: 3
2. At the end of each attributes in an attribute set (a.k.a. dictionaries or key-value pairs) {
a = 1;
b = 2;
}
result: { a = 1; b = 2; }
3. with expressions with pkgs;
coreutils
result: (the coreutils attribute in the pkgs attribute set)
4. Assertions assert a != null;
a
result: (the value of a)
Now, you'll never be confused again.Personally I have nothing against the Nix language, and use it without issue, but it's untrue to suggest that the language itself requires uncommon support for this kind of thing.
Terraform et al, despite not being my favorite, have much simpler semantics than Pulumi. It's not always a good idea to write DSLs into languages with huge paradigm mismatches.
> Why should I care about writing 'new' in front of all my declarative configuration?
Because that’s how your choice of language instantiates an object. Try F# or Swift or Go if it’s that annoying to you.
> What happens when an if statement depends on a concrete value?
What do you think “count = var.concrete_value ? 1 : 0” is doing in Terraform, exactly?
> The leakiness of the abstraction is too terrible to even consider.
While you are are entitled to your opinion, I’d suggest you are very much mistaken, and would implore you to actually consider it for a minute.
Replacing the language requires duplicating all the work that went into Nix, to reach parity, so it is not easy.
That seems like a design flaw in Nix, there's no reason the data model should be so tightly coupled to the scripting implementation that you can't reuse packages written in a different language.
For example, see zb: https://www.zombiezen.com/blog/2024/09/zb-early-stage-build-...
Using a different language to depend on packages derived from .nix would be very much akin to depending on a docker image whose Dockerfile you can not inspect.
Speaking of Docker images and Dockerfiles, that's actually a real-world example of how you can achieve this kind of effect without relying on a specific language. Ironically, you can use Nix to build Docker images; there's a bunch of other alternative builders (e.g. Kaniko, Buildah); you can also just stitch together some files&metadata into tarballs, and then 'docker import' it.
Nix or Guix are of course much more powerful and expressive than Docker images, but there's always a cost to complexity.
If there is something that can be done in the nix language that can't be expressed in the underlying model that needs to be used by another frontend then it should be represented in the underlying model so another frontend can use it.
To put it another way, if you're designing a client-server model where there may be multiple client implementations you don't bake big chunks of the implementation into the clients, you provide it in the server interfaces and data types.
Not having functions as values (true of pretty much any serialization scheme I've ever seen) makes serialized data structures strictly less powerful than data structures in code.
> that needs to be used by another frontend
I don't think this was ever a goal of Nix. But if it was, well, you would end up with something considerably less powerful for the reasons I stated.
If nothing changed, they also have a strong ideological drive and funny support any non-free software.
Also, Guix supports proprietary software just fine. It's just not in the main official repo. But there are other repos that have it, e.g. nonguix.
https://www.gnu.org/software/guile/manual/html_node/Macros.h...
Racket is IMO a pretty compelling environment for prototyping DSLs because of how malleable it can be, so I think the ceiling for ergonomics can be pretty high.
With that said tweag has been working on a kind of nix 2.0 / nix with types for a while with the aim (I think) of being able to use it in nixpkgs: https://github.com/tweag/nickel
Part of that just comes from lazy evaluation, which makes debugging a lot harder in general (you feel this in Haskell...), but also just from nix not being a big popular language that gets lots of polish, and being completely dynamically typed.
It seems inevitable to me that some of the design choices around immutability and isolation are going to result in a larger server image (both on disk and in memory) than if you are prepared to forgo those things. For most people that tradeoff is probably worth it but if you want something to run in an embedded server or with a very low disk footprint it's probably not right for you.
Around 20 years ago people who wanted to do this[2] used to make tiny immutable redhat servers by remounting /usr and a few other things read-only after boot so it's certainly doable but it's a lot more of a pain than what nix does and there is no process isolation and no rollback etc when things go wrong.
[1] ...or generally in fact but that's a matter of opinion and I know people feel differently about this.
[2] me for one, but others also.
Guix is conceptually similar to Nix but uses scheme.
See the 'RATIONALE' document: https://github.com/tweag/nickel/blob/378ece30b3e3c0ab488f659...
The language won't go away and you should try to look at it for more than I don't like it.
The problem isn't the language, the problem is that nixpkgs (and NixOS) are just huge.