NixOS, Haskell LSP in Neovim, XMonad imports are not found - haskell

Here is a screenshot of the problem:
OS: NixOS, unstable channel.
Neovim: 0.7.2.
Haskell LSP: haskell-language-server.
Running xmonad --recompile in the terminal works.
Please help :-)
EDIT 1:
As asked by #ArtemPelenitsyn in the comments below, here is my init.lua: https://pastebin.com/70jMHm02.
The parts that I think relevant:
require'lspconfig'.hls.setup{}
I think it's something more related to NixOS and not to Neovim.
EDIT 2:
As asked by #Ben in the comments below, here is the needed info:
λ ghci
GHCi, version 9.0.2: https://www.haskell.org/ghc/ :? for help
ghci> import XMonad
<no location info>: error:
Could not find module ‘XMonad’
It is not a module in the current program, or in any known package.
ghci>
Here is everything related to Haskell and XMonad in my configuration.nix:
# snip
services.xserver.windowManager.xmonad.enable = true;
services.xserver.displayManager.defaultSession = "none+xmonad";
services.xserver.windowManager.xmonad.enableConfiguredRecompile = true;
services.xserver.windowManager.xmonad.enableContribAndExtras = true;
# snip
environment.systemPackages = with pkgs; [
ghc
haskell-language-server
haskellPackages.xmobar
haskellPackages.xmonad
haskellPackages.xmonad-contrib
];
# snip

In your configuration.nix, you have the following:
environment.systemPackages = with pkgs; [
ghc
haskell-language-server
haskellPackages.xmobar
haskellPackages.xmonad
haskellPackages.xmonad-contrib
];
Here you actually haven't installed a GHC that can access the xmobar, xmonad, and xmonad-contrib packages. Installed you've installed a GHC that doesn't know about any packages, and separately installed those Haskell packages. Any executables in those packages will be added to the PATH environment variable (which is how you can actually run xmonad), but PATH isn't know GHC finds installed packages. You need another step to connect the installation of GHC with the packages, so that you (and haskell-language-server) can import them.
The reason is that the way GHC is expecting to work is that installing packages is a mutating operation on the file system. On a "normal" system you install GHC and it knows about the packages that were bundled with it, then you install another package like xmonad into a folder that GHc will look in for packages1 and now the effect of running that same ghc program has changed.
Nix doesn't like that. Packages are supposed to be immutable in Nix. You can't change a GHC-without-xmonad into a GHC-with-xmonad after the fact.
So just installing pkgs.ghc isn't actually what you want. That package is already completely determined by the nix code that evaluates to, and the package it determines is a baseline ghc with no additional packages. Instead you need to create an entirely new package that consists of GHC installed with xmonad.
Fortunately, this is an extremely common need so there is already a wrapper function to generate this package for you. haskellPackages.ghcWithPackages2. This function takes a single argument, which must be a function you provide. That function will itself be called on a single argument which is the collection of Haskell packages available, and should return a list of which ones you want included in the GHC installation package you're building.
So that means what you actually want is something like this:
environment.systemPackages = with pkgs; [
haskell-language-server
# If you want you can use `with hpkgs; [` to avoid explicitly
# selecting into the hpkgs set on every line
(haskellPackages.ghcWithPackages (hpkgs: [
hpkgs.xmobar
hpkgs.xmonad
hpkgs.xmonad-contrib
]))
];
Under the hood what ghcWithPackages actually does is install ghc and those packages, just as you did, but then it also creates a very small "wrapper package" around ghc that sets environment variables telling it where to find the specific set of packages you installed. The thing that gets added to PATH to provide commands like ghc, ghci, etc is not the underlying GHC, but the wrapped one.3
You don't really need to know any of this "under the hood" stuff, just that every time you need GHC to have a specific set of packages you need to create a new nix package with ghcWithPackages. Knowing that it's based on wrapper scripts can help you not stress about space being wasted though; if you have 100 Haskell projects they can all share any GHC versions and Haskell package versions that are common; it's only the tiny wrappers that you have 100 copies of.
This is also the basic model used by most programming languages that have direct support in nixpkgs (and even some other things that aren't strictly programming languages but can be extended by installing plugins after the fact). It doesn't work precisely the same way for every language, as it depends on what code had to be written around the packaging tools each language has. But the basic conceptual model is frequently something like this.
This is all documented in the nixpkgs manual; as opposed to the manual for Nix itself, or for NixOS. It has a section on Languages and Frameworks where you can find the documentation for how a number of programming language ecosystems are supported in nixpkgs. Although the Haskell section under that has been turned into a small paragraph telling you to go to a separate site for the nixpkgs Haskell docs.
One final note: I'm not 100% sure whether haskell-language-server will just automatically pick up the ghc in your PATH and run with those packages, or if you need any further configuration. Since I am a Haskell developer I have a number of projects that each need different sets of available packages (or even GHC version in some cases), so I don't have any GHC (or HLS) installed at the environment.system-packages level; each of my projects has its own shell environment, ultimately generated from the projects .cabal file. This means I've never actually used haskell-language-server on "loose" Haskell files living outside of a project, and I'm not sure whether you need to do anything more to get it to work. But this is definitely what you need to get ghci> import XMonad to work (without dramatically changing how you do things).
1 And I believe also update some registry files, but I'm not 100% across all of the details. They're not important for this level of explanation.
2 And if you don't like the version of GHC (and everything else) contained in haskellPackages, all the other Haskell package sets also contain this ghcWithPackages function, such as haskell.packages.ghc924, haskell.packages.ghc8102, etc (the top level haskellPackages is one of these sets; whichever is determined to be a good default in the revision of nixpkgs you happen to be using).
3 The environment variables I can see in the wrapper script all have NIX_ in the name, so I suspect the base GHC packages in nixpkgs are patched to support this behaviour.

Related

How does the workflow with Haskell Stack work?

I don't get the point about Stack.
I used to write my Haskell code in my favourite environment, ran or compiled using GHC(i), and if necessary, installed packages using Cabal. Now, that apparently is not the way to go any more, but I don't understand how to work with Stack. So far, I have only understood that I need to write stack exec ghci instead ghci to start a repl.
Apart from that, the docs always talk about 'projects' for which I have to write some yaml files. But I probably don't have any project -- I just want to launch a GHCi repl and experiment a bit with my ideas. At the moment, this fails with the unability to get the packages that I want to work with installed.
How is working with Stack meant? Is there any explanation of their use cases? Where do I find my use case in there?
Edit. My confusion comes from the fact that I want to work with some software (IHaskell) whose installation guide explains the installation via stack. Assuming I already have a GHCi installed whose package base I maintain e.g. using Cabal. How would I have to set up stack.yaml to make stack use my global GHCi for that project?
First, notice that stack will use its own package base independent from cabal. AFAIK they can't be shared... hence, if you run stack build it'll download packages (including the compiler) on its own package database.
Nevertheless stack allows to use a system compiler (but not other libraries). To do so, in the stack.yaml you must have the following two lines
resolver: lts-XX.XX -- keep reading below
system-ghc: True
The version of the stackage snapshot can be found in: https://www.stackage.org/. Each snapshot works with a different version of the compiler. Be sure to use a snapshot with the same compiler version you have in you system. If it happens your system ghc is greater than any lts, then you can set allow-newer: true in stack.yaml.
Now, if getting a different database from stack feels wrong to you, notice that you can build the project with cabal too, since stack at the end of the day spits out a cabal file. Probably, It wont work out of the box if you build with cabal. You can modify the cabal file to match exactly the version of the packages of the snapshot you are using
In summary:
You can use your system-wide ghc
you can not share libraries installed with cabal.
you can use cabal to build the project, probably modifying the ihaskell.cabal file to match versions of the stackage's snapshot.

Haskell Criterion: Could not load module, hidden package

When I do exactly what this Criterion tutorial says to do to get started, I get an error. What am I doing wrong? Is the tutorial wrong? If so, is there a place that I can learn the right way to use Criterion?
Specifically, as the tutorial says, I ran the following in command line:
cabal update
cabal install -j --disable-tests criterion
This ran without error. Then I copied exactly the example program in the tutorial:
import Criterion.Main
-- The function we're benchmarking.
fib m | m < 0 = error "negative!"
| otherwise = go m
where
go 0 = 0
go 1 = 1
go n = go (n-1) + go (n-2)
-- Our benchmark harness.
main = defaultMain [
bgroup "fib" [ bench "1" $ whnf fib 1
, bench "5" $ whnf fib 5
, bench "9" $ whnf fib 9
, bench "11" $ whnf fib 11
]
]
I put that into a file called benchTest.hs, and then I used the command line to compile the program exactly as it says in the tutorial, but with benchTest in place of Fibber, which is what they called it. Specifically, the I ran the following in the command line:
ghc -O --make benchTest
This resulted in this error:
benchTest.hs:1:1: error:
Could not load module `Criterion.Main'
It is a member of the hidden package `criterion-1.5.13.0'.
You can run `:set -package criterion' to expose it.
(Note: this unloads all the modules in the current scope.)
It is a member of the hidden package `criterion-1.5.13.0'.
You can run `:set -package criterion' to expose it.
(Note: this unloads all the modules in the current scope.)
Use -v (or `:set -v` in ghci) to see a list of the files searched for.
|
1 | import Criterion.Main
| ^^^^^^^^^^^^^^^^^^^^^
Short history of Cabal evolution
Through its history, Cabal has gone through a big transformation of how it
works, which is commonly denoted as v1- commands vs. v2- commands; e.g.
since Cabal 2 you can say cabal v1-install or cabal v2-install. What happens
when you say just cabal install depends on the Cabal version: Cabal 2 will use
v1-install by default while Cabal 3 will use v2-install. The change in
defaults reflects the preferred mode of operation. So much so, v1 has become
basically unmaintained. I don't expect it to be removed soon because there is a
group of hard proponents of the old way. But personally I think that, first, the
new way (fun fact you can use cabal new-install as a synonym) is technically
superior, and second, that newcomers should just use it because it's better
documented and you can have more luck in getting help with it (in many cases,
it's easier to help because of the above-mentioned superiority)
Why v1 was subsumed by v2 (in a nutshell)
The main trouble you can run in with v1 is incompatible dependencies across
several projects. Imagine, you work on project A that depends on package X
of version 42, and, at the same time you're starting with project B that
also depends on X but of version 43. Guess what: you can't v1-build the
two projects on the same machine without wiping out cabal's cache in between.
This was the way it worked in dark ages (from the middle of 2000s to yearly
2010s).
After that, cabal sandboxes arrived. They allowed you to build our imaginary
projects A and B with less hussle but the interface was not great and, more
importantly, every sandbox was independent and, therefore, held a big chunk of
repetitive binaries; e.g. A and B could depend also on Y of the same
version 13, so there's theoretically no need to build and store Y twice,
but that's exactly what cabal sandboxes would do.
Cabal v2 arrived in the late 2010s and brought exactly that: isolation between
projects via an (also recent) GHC feature called environment files and sharing
of build artifacts via Cabal store (so that you don't store many copies of the same thing).
Environments and v2-install
You can create a separate environment for
every project (A, B, etc.) by doing
cabal v2-install --lib X-42 --package-env=.
in the directory of the respective project. Couple notes on the syntax:
v2- can be omitted in Cabal 3 because it's the default;
The order of flags is not important as long as install goes right
after cabal;
--lib is important, because by default you get only executables (that's
what happens with criterion: the package holds an executable);
--package-env=. means: create a GHC environment file in the current
directory (hence .). If the command succeeds you will notice a new
("hidden" on Linux) file in the current directory, named something like
.ghc.environment.x86_64-linux-9.0.2. This is the file that tells all
subsequent calls to GHC in this directory where to search for the libraries compiled by Cabal
and stored in… Cabal store (on Linux it's the ~/.cabal/store directory by
default). In principle, you can use other than . values for environments,
and if the value doesn't correspond to a path, it will be a named
environment. More details in Cabal reference manual… In practice, I find
99.99% cases perfectly served by --package-env=..
X-42 means the package X of version 42 should be added to the newly
created environment. You can omit the version (you will get "some compatible
version"), and you can list more than one package.
What cabal v2-install --lib means if no environment specified
It means the default environment. There is a single shared environment called
default. It has the same very problem that v1 had (see above). So, in
practice it could work but it will be very fragile, especially if you get into
the "project A and project B" situation described above. Even if you only
work with one project now, I suggest using --package-env because it's future
proof.
Why the initial error
As you say, you were using Cabal 2 and therefore v1-install initially and saw
the dreaded "hidden package" error — what's the reason for this? Honestly, I
have no idea. And I doubt it's easy to figure without rolling back to that older
Cabal version and experimenting more. As I say above, v1 is not really maintained
anymore, and even if it looks like a bug in Cabal (which is perfectly possible
especially with earlier releases in the Cabal 2 line), no one will probably
bother about it.
Isn't it sad that old tutorials don't work anymore
It is. Unfortunately, the software technology has to develop to make the world a
better place to live (see the reasons for v2 above again). Sometimes this
development has to break backward compatibility. Ideally, we'd go and update all
educational materials and manuals to reflect the change but that's hardly
possible. Sigh. New users of Haskell has to be careful and creative with respect
to the v1 to v2 shift and try to get a basic understanding of v2 early on
and try to apply it to the good but old tutorials that are still out there.
Are environments the best approach?
Some of the designers and proponents of v2 argue that environment files are a
too subtle of a feature. As a ("proper") alternative, they suggest creating a
full-fledged cabal package for every project you start. This amounts to calling
cabal init, which will create a <project-name>.cabal file in the current
directory, and maintaining the .cabal file, including the list of package
dependencies there; you will also use cabal v2-build to build the project (instead of directly calling GHC).
While more robust, unsurprisingly, this idea doesn't sleep
well with many people who use Haskell to try a lot of small independent things:
it feels lame to create a whole "package" every time. Well, it's just one extra
file in practice, and it's not even extra if you compare it to the
environments-based approach I described above, which also maintains one extra
file, but in that case you don't ever need to edit it by hand (unlike the
.cabal file). All in all, in the scenarios of "trying one small thing" I find
the environments-based approach working better for me. But it does have its
limitations compared to the package-based approach, notably, it's hard to figure out
how to get profiling versions of dependencies in the environment. But that's a
story for another day…
You can find more discussion about how cabal v2-install --lib can be improved in Cabal issue.
If you want to follow the officially blessed way of doing things (i.e. via a package), please, take a minute to read the Getting Started section of the Cabal manual — it's very clear and shows exactly an example of simple application with a dependency on an external package.

What is the suggested way of setting up Haskell on Archlinux?

Long story short, I'd like some guidance on what's the (best) way to have Haskell work on Archlinux.
By work I mean all, in terms of the ghci command line tool, installing packages I don't have - such as vector-space, which this answer to a question of mine refers to -, and any other thing that could be necessary to a Haskell obstinate learner.
Archlinux wikipage on Haskell lists three (alternative?) packages for making Haskell work on the system, namely ghc, cabal-install, and stack. I have the first and the third installed on my system, but I think I must have installed the latter later (unless it's a dependency to ghc) while tampering around (probably in relation to Vim as a Haskell IDE). Furthermore, I have a huge amount of haskell-* packages installed (why? Who knows? As a learner I must have come multiple times to the point of say uh, let's try this!).
Are there any pros and cons ("cons", ahah) about each of those packages?
Can they all be used with/without conflicts?
Does any of them make any other superfluous?
Is there anything else I should be aware of which I seem apparently ignorant about based of what I've written?
Arch Linux's choice of providing dynamically linked libraries in their packages tends to get in the way if you are looking to develop Haskell code. As an Arch user myself, my default advice would be to not use Arch's Haskell packages at all, and instead to install whatever you need through ghcup or Stack, starting from the guidance in their respective project pages.
You are basically there. Try the following:
ghci: If you get the Haskell REPL then it works.
stack ghci: Again you should get the Haskell REPL. There are a lot of versions of GHC, and stack manages these along with the libraries. Whenever you use a new version of GHC stack will download it and create a local installation for you.
stack is independent of your Linux package manager. The trouble is that your distro will only have the Haskell libraries it actually needs for any applications it has integrated, and once you step outside of those you are in dependency hell with no support. So I recommend that you avoid your distro Haskell packages. stack does everything you need.
If you installed stack from your Linux package manager then you might want to uninstall it and use a personal copy (i.e. in your ~/.local directory) instead. Then you can always say stack update to check you have the latest version.
Once you have stack going, create a project by saying stack new my-project simple. Then go into the project folder and start editing. You can work with just a .hs file and GHC if you really want, but its painful; you will do much better with stack, even if you are just messing around.
You'll also need an editor. Basic functionality like syntax highlighting is available in pretty much everything, but once you get past Towers of Hanoi you are going to want something better. I use Atom with ide-haskell-ghcide. This uses the Haskell Language Server under the hood, so you will need to install that too. I know a bunch of other editors have HLS support, but I don't have experience with them.

What is Cabal Hell?

I am a little bit confused while reading about Cabal Hell, as the term is overloaded. I guess originally Cabal Hell referred to the diamond dependency problem, which was solved by restricting the build plan to have only a single version of any package in each build plan (two different versions of a package can't exist in a single build plan) as explained in this answer.
However, the term is also used in various other contexts. Such as destructive re-installations, incorrect package dependency boundaries (lower/upper version bounds), inconsistent environments ... (or any other error reported by Cabal).
Particular among these, I am confused about 1) destructive re-installations and 2) inconsistent environments? What do they mean, and how cabal new-build solves these problems (is it just sandboxing like cabal sandbox)? And what role ghc-pkg has to play here?
Any references or a simple example where these problems could be reproduced would be very appreciated.
Regarding "destructive re-installations": If I am not wrong, GHC has a package manager of itself (ghc-pkg), and the packages are installed as dynamically linkable libraries i.e: base depends on ghc-prim, so if ghc-prim is removed it will break base, am I right? And since GHC only allows one instance of a package with the same version, cabal install might register a newer build of the same (package, version) such that it breaks the dependents of the unregistered package. If the above understanding regarding "destructive re-installations" are correct; how does cabal new-build help here?
The only meaningful use of the term is the one given in the linked answer. Related are the follow-on problems from having lots of different packages in the global database, which can make encountering diamond dependencies more common, requiring destructive reinstalls to resolve, etc.
The other usages of the term are not helpful and just mean "problems somehow involving cabal."
That said, let me answer your other questions.
1) ghc-pkg is not a package manager, but rather a tool for managing ghc package databases. It is used by cabal to register packages into databases, and can be used by end-users to inspect the contents of the databases. Think of it as part of the underlying substrate provided by ghc, not a competing tool.
2) new-build eliminates and replaces the standard notion of a packagedb entirely. Instead of saying that a db consists of packages and versions, with at most one of each pair, instead a db consists of potentially many copies of packages at any given version, each with potentially different versions of its dependencies, all of which are managed in part by hash-addressing, so marked by a unique "fingerprint". This is called the store. When you new-build, cabal calculates a build plan irrespective of any previously installed dependencies, from scratch. If a particular fingerprint (consisting of a package, version, and the versions of all its dependencies, certain flags, etc) already exists in the store, then it makes use of it. If it does not, it calculates it.
As such, the only "diamond dependencies" that can occur are the truly insoluble ones, and not the ones occasioned by having fixed too-early (due to already-installed deps) some portion of the dependency tree.
tldr; you write "since GHC only allows one instance of a package with the same version" but new-build partially lifts this restriction in the store which allows the solver to produce better, more reproducible plans more often.

How to upgrade Haskell Platform

I have the Haskell Platform 2012.4 installed on Windows. I would like to try the new extensions in GHC 7.6.2. It looks like the GHC 7.6.2 x86_64 download does not include an installer and is just the binaries. What is the proper way to get the latest version installed and set as the default for building Haskell projects? Thanks.
If you download GHC itself, you just get GHC and a tiny handful of libraries. If you download the Haskell Platform, you get GHC plus a much bigger collection of libraries. However, the Haskell Platform is updated infrequently, so you'll get an older version of GHC.
If you're asking "how do I install the Haskell Platform and then make it work with a newer GHC?", then the answer is "you don't". Haskell libraries have to be compiled for the specific version of GHC you're using.
You basically have two options:
Use the Haskell Platform, together with the version of GHC that it supplies.
Use the latest GHC, and compile whatever libraries you want manually.
On Linux it's not too bad, but Haskell libraries that bind to external C code tend to be fiddly to build on Windows. (Stuff like OpenGL, zlib, etc.) Packages that are 100% vanilla Haskell code are drop-dead easy to compile on any system.
I haven't done this and I'm at work so I can't test it out, but looking at the GHC docs I would think you can use the --with-compiler=path flag to select which version of GHC to use?
See also this question, related to using cabal with multiple versions of ghc installed. I would guess that you probably want to use cabal-dev or something to sandbox this, otherwise your package database may become a mess.
EDIT: As far as a default, I think you can set that in a cabal configuration file. See the comments to the accepted answer in that question I linked.
The other answers here are great, and I wanted to add that the current best way to get the latest version of GHC installed is to look at haskell.org's installation instructions. I bounced between lots of different options before I landed there, and I think it's the best source of truth from what I can tell.
To summarize the current instructions: if you already have chocolately set up, "at an elevated command prompt, run choco install haskell-dev, followed by refreshenv."

Resources