This is a very good question, and we are exploring this at this very moment.
Currently, we are experimenting with the least invasive and simplest alternatives, that would allow you to write just one derivation or one NixOS module in Nickel, without having to implement implement stuff either in Nix (I mean, in the package manager itself) or to re-implement the Nix features you mention on the Nickel side.
For example, you could write a shell description in Nickel (
shell.nickel). Then, your
shell.nix calls to a generic Nix library function that runs Nickel to generate a JSON, then imports said JSON and return the corresponding shell. This is not totally as simple as it sounds and a bit hacky (just specifying
buildInputs can’t be done just in JSON, so the Nix import code actually has to do some parsing to rebuild the inputs), but is done easily in a very direct way.
Another way would be to have good languages interoperability, for example being able to import nix expressions directly in Nickel and to transpile Nickel values (once evaluated) to Nix expressions. Doing so, we can still leverage everything that already exists in Nix without having to re-implement it on the Nickel side.
Lastly, the most involved ones - but maybe also the most powerful - would be to either add Nickel support directly in Nix, which would accept Nickel sources in addition to Nix expressions, or the other way around, as you described: re-implement Nix features on the Nickel side such that a Nickel file would be able to evaluate all the way down to a derivation.