I might suggest making a typed superset of Nix, basically adding record types and function signatures. That in itself would be a huge boon for tooling and interacting with nixpkgs. nixpkgs describes a huge and varied meta-API for derivations in various build ecosystems, which makes it hard to use because you ultimately need to dive into source code.
We've heard this feedback pretty frequently. We have a few ideas of ways to add some types to Nix in place. I think even simple naive approaches would add a lot of safety and improve the usability of Nix.
However, one of the bigger problems here is the general API design of Nixpkgs. Most of the function interfaces use some named parameters and then use `...` to accept any additional parameters. All of those then get passed down to some other function, and on and on. This makes a mess! It is hard to tell what functions use what parameters, and none of them can restrict their interface.
We could see some significant DX here by creating smaller, more specific interfaces that actually restrict their inputs.
I think the biggest win with some progressive typing would be better error diagnostics. Right now the errors are horrifyingly bad in many cases, puking out massive recursive stack traces that have nothing to do with the real problem. Being able to pinpoint exactly where the square peg is going into a round hole would be amazing.
Some community members have looked into this quite a bit, and personally I have come to the conclusion that types don't really give that much benefit to Nix (and I say this as a Rust & Haskell developer).
Nix expressions are almost always[2] short-lived snippets that evaluate to a data structure, not long-running programs where the distinction between "static analysis time" and runtime is extremely relevant.
In fact, deriving the full set of potentially relevant type constraints is only possible at runtime due to how the import system works and the shape of most expressions. If you pick a single file from nixpkgs without context, you can't get much information from it at all (other than a lot of unknown types being passed to things that accept an unknown type) - it's only in the context of an evaluation of a graph node that uses that file that you can infer anything meaningful about it (and even then that information is only relevant in that context).
I think what people actually want is a way to derive more sensible documentation information from the code so that the various builders etc. could have checked documentation pages, as well as tooling for auto-completing members of attribute sets, names of (formals) function arguments and so on.
fwiw, there's an alternative implementation of the Nix language[0] that we (TVL[1]) are slowly open-sourcing at the moment, and we're aiming to design it in such a way that things like an LSP can be implemented on top of it, and to be able to dump out static information from a context that can help with documentation etc.
> If you pick a single file from nixpkgs without context, you can't get much information from it at all (other than a lot of unknown types being passed to things that accept an unknown type) - it's only in the context of an evaluation of a graph node that uses that file that you can infer anything meaningful about it (and even then that information is only relevant in that context).
This is exactly where types would be the most beneficial, and what makes nix so hard to read. If a nix function could specify the types of its arguments, then I might have a chance of better understanding it.
Just because most nix functions currently don’t have many constraints on their inputs doesn’t mean that they couldn’t in the future.
> it's only in the context of an evaluation of a graph node that uses that file that you can infer anything meaningful about it (and even then that information is only relevant in that context)
That’s how nixpkgs works now. But we can make functions that work only in more limited contexts.
> tooling for auto-completing members of attribute sets
That's a great start, but it would be nice to know what the expected type of the member is! Often it's another attribute set, a list, or even a function, and it's impossible to tell what type is expected without looking at where the member is used.
I am originally opposed to blaming the Nix language, but now that I read your comment and thought a bit more about it, Clojure’s spec might be a much better tool than types. It would solve the readable error messages problem quite well and is flexible enough to accommodate any usage like parameter passing.
However, this has massive runtime cost (especially in hot code paths), so some sort of spec-like annotation system would be cool. We'll see how it shakes out over time ...
As for error messages, we might already be able to do big improvements in Tvix. Our bytecode has pretty exact tracking of the source spans that things come from all the way throughout, so we could trace things like a type error being raised somewhere to the last time that value was passed through as a function argument from user code etc.
This error message business will be a whole area of development of its own however, for now we're just making sure that as much of the relevant information is available as cheaply as possible.