Guix is nix via scheme, why no nix via Haskell?

I wonder why is there no nix using Haskell in the sense as guix is using guile scheme to archive the functionality of the nix package manager? This question is not too serious. As it is usually too much work to start something like that. But I ask because I might miss something about nix language and nixpkgs that makes the use of other languages difficult.

4 Likes

There are a few Haskell/Nix-related projects you might be interested in:

  • hnix: a Haskell implementation of a bunch of things related to Nix, including the Nix language and ways to interact with the Nix store. There are a few commonly(?)-used Haskell packages that depend on hnix for parsing Nix code.

  • purenix: a compile-to-Nix language in PureScript (which is very similar to Haskell). This may be closest to what you’re looking for. Blog post here. This is surprisingly usable, although there are no real users yet. Much of the ecosystem would still need to be built out.

  • dhall-nix: a way to compile Dhall to Nix. Blog post here. I’m not aware of this being used anywhere, but it is pretty neat if you’re already a Dhall fan.

  • nickle: This is looking to be the next “official” Nix language. It is a gradually-typed language that is somewhat similar to the current Nix language. It is not currently production-ready, but I wouldn’t be surprised if it sees some adoption in the next 2 to 3 years. It is not related to Haskell at all, but it is at least Nix + types.

As far as I know, there is nothing exactly like what you’re asking about (which is somewhat surprising). You could easily imagine someone coming up with a new language that compiles to Nix’s .drv files in the Nix store, and just relies on the Nix builder, similar to what Guix is doing. But I haven’t heard of any projects even attempting this.

10 Likes

Haskell is compiled which makes little sense for nixpkgs.

Correct me if I am wrong, but can’t Haskell be interpreted as well as compiled? Otherwise I’d be interested to know how GHCi along with runhaskell work. Given that Guile can also be both compiled and interpreted I’d be interested in knowing what an embedded DSL brings as a disadvantage here; I always was curious why something like Haskell wasn’t used in the first place and what problems might that have caused with nixpkgs. It honestly seems to me that having Nix be its own DSL brought more problems than benefit; I feel like Guix took a much better approach here.

1 Like

“Interpretation” works by compiling whatever you need first and then executing it, you can observe how this would perform with a larger project as nixpkgs is by opening cabal v2-repl in even a medium sized Haskell project.

This question doesn’t really makes sense. Even though I hadn’t even started elementary school when the Nix project was conceived, I’m fairly certain that Haskell wasn’t really something that was considered at the time. It is hardly productive to project the current state of the GHC and guile (work on Guix started in 2012) projects back to before 2006.

Apart from that it’s easy to enumerate reasons (especially) against Haskell, even if writing a functional lazy programming language in addition to a package manager may not have been the soundest choice. Overall I think these hypothetical scenarios about what if language X was used are hardly productive: Fact is we have Nix already and a huge and extremely valueable project written in Nix (nixpkgs) that you’d be sacrifizing if you tried switching to another language. Besides, Nix with an alternative language already exists, it’s called Guix and has a much smaller package repository.

3 Likes

I understand that most interpreters work by compiling source code to bytecode to be executed by the runtime environment, I suppose I was asking more what makes Haskell different here since GHCi (as far as I know) functions in the same way as most other interpreters I know. I only ask because of your initial response:

which is confusing to me because it doesn’t seem to have to be directly compiled by GHC but can be interpreted instead with GHCi (although I could very well be missing something!).

I ask these questions less to bash on what we have now and more to try to understand why the particular path of design we have now was chosen. I feel like there are problems/friction that myself, other community members and people looking to give nix/nixos a try consistently run into because decisions like not having an embedded DSL were chosen and are not trivial to solve without risking an entire rewrite or rebuild on something like nixpkgs as you mentioned. Was there an actual design consideration made where an embedded DSL was not an option (Haskell does not have to be the only option here. Was there really no langauge before 2006 that could implement at the time what the Nix DSL implemented?)? If embedded DSLs were avoided for a particular reason, then I’d be interested in knowing more of the details of that even if it was only a consideration made because of the time nix was originally developed in. At least then I could have a better understanding of Nix’s design and development up until this point.

Yes I am aware of this, and I think Nix could learn quite a lot from Guix which is why I don’t necessarily think this line of questioning is completely counter-productive. I don’t think the best take here is to say “Well if you don’t like it go use the alternatives!”, but rather look at what other approaches are doing and adopt what they’ve done better. Of course in this scenario it’s much harder to say this for Nix than Guile given the differences in flexibility/adaptability between the two, but even then I am sure there is a conversation to be had about Guile’s language features in consideration to future Nix projects like Nickel. Of course there are other topics as well like Guix’s tooling and documentation style but these are definitely outside the scope of this discussion.

3 Likes

Under this interpretation there is no fundamental difference between compilers and interpreters, just the tradeoff made when implementing it. FWIW I’m willing to bet that the current (pretty slow) Nix evaluator would easily outperform GHC(i) for a similarly structured and sized Haskell program to nixpkgs when »interpreting« it.

In general interpreters will be optimized to have a low startup time for acceptable performance where compilers can take their time to produce a faster runtime executable.

I suppose you’ll have to ask Eelco about this. My suspicion that this was not really a point of consideration is at least supported by the fact that the Nix PhD Thesis doesn’t go into the why of implementing a functional language, just into some of the chosen properties (FWIW a practical lazy, dynamically typed language that is embeddable was probably not available at the time).

Sure, I was not implying otherwise.

Nickel is not a future Nix project and I highly doubt that it’ll replace the Nix expression language at any point.

1 Like

I was going to say something similar, though I do note that there’s a little more ~motivational text in 10.3. Related work which starts on numbered-page 240. The thesis does mention Haskell a few times.

I think for the original implementation it is clear that Eelco wanted a dynamically typed language. (Language Servers improved the practicality of typed languages a lot and haven’t been around at that time.) The thesis mentions Haskell surprisingly little. Primarily because other projects used Haskell to implement a compiler for their project. I myself don’t know enough about ghci’s performance, but maybe that’s a bottleneck all seasoned Haskell developers know enough about to not attempt a guix like project. Even i would be surprised if guile would be significantly more performant on huge data structures then ghci.

The issue is not performance, it is startup time. GHCi’s startup time is terrible as is to be expected with any compiled language.

One other key factor about Nix is that imports are lazy as well, something Haskell can’t do. Given that nixpkgs is basically a huge library of which you only need to evaluate a certain subset for every instantiation you want, the ability to only evaluate the needed parts is critical.

4 Likes

imports are lazy as well, something Haskell can’t do

I guess that’s the blocking issue to use Haskell.

Just out of interest rather than for Nix, it can’t really be impossible to do lazy imports with Haskell, can it?

I mean you can use the GHC API to load modules at runtime. But that’s messy and definitely not part of the design of the language itself. But import lines at the top of your file? Yea, GHC will build/load those modules before yours.

3 Likes

Thank you! Interest satiated.