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).
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.
With a default config you start with a console, systemd, dbus and some things to make it boot. There is barely anything.
`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.
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.