Towards a Common Haskell Development Environment
One of the projects we have been pursuing at the Haskell foundation is a unified Haskell installer. Hitherto things have been a little fragmented when it comes to Haskell installers, not covering Windows very uniformly and, of course, requiring different installers depending upon whether you want to work with
cabal-install. (I distinguish between the
Cabal API used by both
stack and the
cabal-install application, using the package names to keep everything straight.)
This is really unfortunate for many reasons.
Poor Windows coverage restricts access to Haskell, setting up a viscous cycle whereby the poorer the Windows tooling, the smaller the pool of Windows Haskell developers available to improve or even maintain it.
It reinforces a tendency to silo developers with those using Posix/Windows or
cabal-install, contributing to a growing incomprehension of each other’s problems, even for those that actively want to see more uniformity in development environments. It is difficult to overstate the problems this causes.
Even for those that are prepared to build their own unified development environment it is not currently practical to do so based on the commonly used tools — unless you are prepared to patch the tools and/or install wrapper scripts (see below) as the current tools will install toolchains without regard those that are currently installed. It is of course easy enough to install one along side the other for a specific use but they will manage their own duplicate toolchains without constant intervention.
Writing satisfactory Haskell introductory tutorials is nearly impossible to do. You have to either pick one of the build systems and cover that properly, most likely on Posix, or try to to cover everything in detail which will immediately restrict your audience, or abstract away from the details and your audience will get culled when it comes to trying things out.
This list isn’t comprehensive and you can probably add to it.
How to fix?
[Update 2021-07-14: I should have made clear in the original posting that what follows is merely my own technical proposal for integrating
stack and not in any way a Haskell Foundation adopted proposal.]
Given the above, ideally, all Haskell installers like
ghcup should install both
stack in such a way that they will use a toolchain that either installs, and while allowing the developer to control which tool-chain installer gets invoked regardless of whether you are building with
cabal-install from a clean checkout. (Currently only
stack will auto-install a missing toolchain but there is no reason why
cabal-install could not trigger an installation with the right hooks.)
Cross-installing the build tools is almost trivial (
ghcup is already unofficially doing it) — getting them to manage the toolchains coherently thereafter is the hard part.
A pragmatic approach could be taken as
ghcup each use entirely straightforward and stable organisations of their installed toolchains. Before each is about to install another toolchain they could check whether the other had already installed it and link it in rather than downloading and installing another GHC bindist. Perhaps predictably, nobody was keen on this solution, as they would be relying on undocumented interfaces going forward, clearly risking problems down the line.
I have come to see this is correct — we should build proper interfaces. It also opens up the opportunity to give the developer full control of which system manages the toolchain installations.
hs: a tooolchain installation broker
stack wish to locate a toolchain (say,
ghc-8.10.4), and they have been configured to do so, they will call out to a new
hs broker service:
(Here we are
execing hs, but an internal
hs library call is also available.)
hs responds with the location of the root of the installed bindist if it is already installed (in the case above, we clearly have a stack managed toolchain).
If the toolchain is not installed then, depending upon how
hs is configured, it will yield an error indicating that no such toolchain could be located, or it will auto-download-and-install the toolchain with the designated default toolchain installer, or it will ask the developer if they would like to download and install the required GHC bindist.
The build manager can specify this behaviour explicitly.
hs: a PATH interface for cabal-install
cabal-install uses the
PATH to resolve the location of the tools. If
ghc-pkg for GHC 8.10.4 is required then
ghc-pkg-8.10.4 will be
execed. Exploiting this we can install a bunch of wrapper scripts on the
PATH containing variants of the following:
Instead of yielding the location of the bindist
hs run ghc-pkg-8.10.4 runs the command directly passing through all of the arguments after the
-- without further interpretation. The wrapper scripts can rely on the configured installation behaviour or specify it explicitly.
Of course, for this to work these wrapper scripts (in
~/.hs/bin by default) must be on the path ahead of anything else that might have
ghc-pkg-8.10.4 and friends.
hs and listing installations
The installation manager priorities, the default install mode and default toolchain can be configured or displayed (by specifying no argument) as follows:
To list the installation in your development environment:
$ stack list 8.6.5 stack /Users/chris/.stack/programs/x86_64-osx/ghc-8.6.5 8.8.3 stack /Users/chris/.stack/programs/x86_64-osx/ghc-8.8.3 8.8.4 stack /Users/chris/.stack/programs/x86_64-osx/ghc-8.8.4 8.10.4 stack /Users/chris/.stack/programs/x86_64-osx/ghc-8.10.4 8.10.5 ghcup /Users/chris/.ghcup/ghc/8.10.5 9.0.1 ghcup /Users/chris/.ghcup/ghc/9.0.1
configuring the build tools
stack will use its internal GHC installations while
cabal-install will rely on the path. It would be great if their global configurations would allow the developer or the bootstrap Haskell installer to configure them to defer to specified broker as follows:
This would be bigger departure for
cabal-install but it should be straightforward for
cabal-install to make a call out to the broker when the need for a sandboxed toolchain is discovered with a configured broker, at which point
cabal-install could internally put the toolchain located by the broker on its path, thereby – when a broker is configured – relieving the developer of the need to manage the
PATH to capture the relevant toolchain.
Both build systems would have converged on the same behaviour when it comes to toolchain management.
a working prototype
hs written, a patch for
stack and wrappers for
cabal-install, yielding a unified system in which all of the toolchains are made available to both systems as installations are demanded and added by
ghcup according to the
hs configuration. (This is Unix-only for now – Windows coverage should be straightforward and is in progress.)
Got an issue with any of this? Please share or drop me a line (see below).