Trying to get Nix Flakes / Haskell / HLS / VSCode to work, but nothing works properly

Let me give you a couple high-level tips first:

  • A lot of people who are pretty familiar with Nixpkgs spend a lot of time looking through the Nixpkgs source code. If you want to build up a good understanding of how Nixpkgs works, and be able to look up answers to your own questions, you’ll likely want to get in the habit of grepping through Nixpkgs for things. I know this can be quite annoying at first, but it is quite common if you’re really trying to get a good handle on Nixpkgs.

  • I don’t know if I’d call things like buildInputs vs nativeBuildInputs, prev vs final, packages from the top-level vs from internal package sets, etc as internal or esoteric, but I get what you’re trying to say. Depending on what you’re trying to do, using either buildInputs or nativeBuildInputs may work. Using either prev or final may work. Pulling packages from the top-level vs internal package sets may work. But when myself and others write examples and code snippets, we try to do the “most correct” thing, even if it is a little more confusing. You’re of course always welcome to question why something is written like it is!

And now to answer your actual questions:

buildInputs is for libraries that are used a run-time. nativeBuildInputs is for compilers/tools that are run at build-time. It is sort of hand-wavey for something like HLS, which both needs to run at “build-time” (since you’ll be running it in your dev environment), but also needs to be able to link into your own Haskell code.

As long as you’re not cross-compiling, it is often okay to mix up buildInputs vs nativeBuildInputs. (But there are a non-insignificant number of people who use Nixpkgs for cross-compiling, so you will often get corrected in PRs and forum posts if you mess it up.)

Nixpkgs contains “top-level” derivations, that are all mostly defined in this single file:

https://github.com/NixOS/nixpkgs/tree/e608c90a1cf381dde6ac9e0f085337150f2af3e2/pkgs/top-level/all-packages.nix

If you grep through this file, you should find an entry for haskell-language-server. This is the “top-level” haskell-language-server. It is directly under final.

Nixpkgs also contains sub-package sets. haskellPackages is an example of a sub package set. This gets a little more complicated, but things in haskellPackages are effectively defined here:

(You should also be able to find haskellPackages defined in the above all-packages.nix file)

If you dig into how final.haskell-language-server is defined, vs how final.haskellPackages.haskell-language-server is defined, you’ll see that there are some minor differences, but it mostly doesn’t matter which one you use.

However, you do have to be sure to use haskell-language-server that was compiled with the same GHC version as you’re using for your project. So if your project uses haskell.packages.ghc94 instead of haskellPackages, you’ll need to make sure to use haskell.packages.ghc94.haskell-language-server instead of the top-level haskell-language-server, or haskellPackages.haskell-language-server.

This is just sort of how haskellPackages.shellFor works. There is an example in the source code:

Although now that you mention it, it is kind of confusing that this doesn’t throw an error, or at least a warning or something.

GHC has a concept of a package, and a package database:

Cabal and cabal-install work directly with GHC packages and package databases, but give users a high-level tool over-top of them. Most users aren’t really directly aware of GHC package databases, but just work with them through cabal-install.

Basically what Nix does is build a GHC package database, and directly hands it to Cabal. Cabal is able to use it without having to build any dependencies on its own. For your nix develop shell, if you run ghc-pkg list, you should see what this looks like.

3 Likes