Haskell

From ArchWiki
Jump to navigation Jump to search

From Wikipedia:

Haskell is a general-purpose, statically typed, purely functional programming language with type inference and lazy evaluation. Developed to be suitable for teaching, research and industrial application, Haskell has pioneered a number of advanced programming language features such as type classes, which enable type-safe operator overloading. Haskell's main implementation is the Glasgow Haskell Compiler (GHC). It is named after logician Haskell Curry.

Installation

Haskell generates machine code that can be run natively on Linux. There is nothing special required to run a binary (already compiled) software, like xmonad or pandoc provided in the official repositories. On the other side, building AUR packages or developing software require a compiler and build tools to be installed.

To install the latest version of Haskell, install the following packages from the official repositories:

  • ghc (GHC) — A Haskell compiler. There are several implementations available, but the one used most (which is now de facto the reference) is the GHC (Glasgow Haskell Compiler).
  • cabal-install (Cabal) — A build tool focused on dependency resolution and sources packages from Hackage. Hackage is the Haskell community's central package archive of open source software.
  • stack (Stack) — A build tool focused on curated snapshots and sources packages from Stackage. Stackage is a stable subset of Hackage that provides curated sets (snapshots) of packages known to work well with each other. Alternatively, Stack can be installed through stack-staticAUR package. It provides statically linked binaries, thereby avoiding dozens of haskell-* dependencies.

You can install and configure either Cabal or Stack or both of them. Most of the popular Haskell packages support both Cabal and Stack.

Note: There are other ways to install Haskell on Arch that do not use packages from official repositories and might be easier for beginners. See #Alternate installations for more information.

Configuration

Since version 8.0.2-1, the Arch ghc package and all haskell-* packages in community provide only dynamically linked libraries. Therefore, to link successfully one must configure GHC, Cabal and Stack for dynamic linking, as the default is to use static linking.

Using dynamic linking typically results in faster builds and smaller disk and RAM usage (by sharing pages between multiple running Haskell programs), and will free you from troubleshooting cross-GHC mixing errors. But it has its own disadvantage: all tools you install from source will break on every update of ghc, ghc-libs or haskell-* packages since libraries compiled with GHC do not provide a stable ABI. When running such broken binary, you will see the usual message error while loading shared libraries: libHS...so: cannot open shared object file: No such file or directory. To fix this, just rebuild and reinstall the broken tool in order to relink it to newer libraries.

On the other hand, static linking is generally easier to maintain and does not force you to rebuild all tools from source after every update of their dependencies. For these reasons, static linking is often the preferred option for local development outside of the package system. If you prefer static linking, see #Static linking or #Alternate installations for details.

Note: For a detailed explanation of why Arch moved to dynamic linking for Haskell packages, see this maintainer's response.

Invoking GHC directly

In order to link successfully one must pass the -dynamic flag to GHC. You can try it with the following file:

Main.hs
main = putStrLn "Hello, World"

Compile and run it with:

$ ghc -dynamic Main.hs
$ ./Main
Hello, World

Configuring Cabal for dynamic linking

First, run the following command to download the latest list of packages from Hackage and create global configuration file ~/.cabal/config (or the file $CABAL_CONFIG points to):

$ cabal update
Tip: It is advisable to periodically run cabal update to synchronize your local list of packages and dependencies with the newest version on Hackage.

To configure Cabal for dynamic linking, uncomment and edit the following options in ~/.cabal/config:

~/.cabal/config
library-vanilla: False
shared: True
executable-dynamic: True
program-default-options
  ghc-options: -dynamic
  • library-vanilla: False suppresses the creation of static libraries (if your project contains a library).
  • shared: True enables the creation of shared libraries (if your project contains a library).
  • executable-dynamic: True causes dynamic linking to be used for executables (if your project contains executables).
  • ghc-options: -dynamic adds the -dynamic flag to every invocation of GHC (e.g. if a package has a non-trivial Setup.hs).

Configuring Stack for dynamic linking

You can use stack setup command to initialize Stack and create global configuration file ~/.stack/config.yaml. By default Stack will automatically download its own version of GHC to an isolated location upon first invocation. To force Stack to use system GHC installation instead, run stack setup with --system-ghc and --resolver flags:

$ stack setup --system-ghc --resolver resolver

Note that you need to specify a resolver which is compatible with your system GHC. Otherwise Stack will happily ignore --system-ghc flag and download its own copy of GHC. You can determine the version of system GHC using ghc --version command:

$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 8.10.2

Then visit Stackage website and pick a suitable Long Term Support (LTS) or nightly snapshot matching your system GHC version. Use the selected snapshot for --resolver flag on the command line, e.g. --resolver lts-16.15 or --resolver nightly-2020-09-01.

Stackage typically lags behind new GHC releases. It may happen that no Stackage snapshot for your system GHC has yet been released. In this case you might want to choose a snapshot for some earlier minor version of GHC or temporarily downgrade your Haskell installation and wait until support for newer GHC releases finally lands on Stackage.

To configure Stack for dynamic linking, add the following snippet to ~/.stack/config.yaml:

~/.stack/config.yaml
# Stop downloading GHCs into isolated locations under ~/.stack.
install-ghc: false

# Allow Stack to pick the system GHC (false by default).
system-ghc: true

# Allow to use, say, Stackage snapshot for GHC 8.8.2 with system GHC 8.8.3.
compiler-check: newer-minor

# Add the -dynamic flag to every invocation of GHC.
ghc-options:
  "$everything": -dynamic

Package management

Most of Haskell libraries and executables are distributed in units of source packages available from Hackage and Stackage.

As is common in other compiled languages, a number of popular Haskell packages are available from official Arch repositories in pre-built form. Some additional packages can be installed from AUR.

Although it is recommended to use pacman to install GHC, libraries and tools from official repositories — at some point you may want to install Haskell packages directly from Hackage/Stackage or compile your own (or somebody else's) packages from source. You will need Cabal or Stack for doing that.

The following table summarizes the advantages and disadvantages of different package management styles.

Method Pros Cons
Official repositories Provided by Arch Linux developers, consistent versions of packages, already compiled Not all packages available, only dynamic libraries available
Cabal All packages available, root not required Installed in home directory, failures in dependency resolution, difficult to remove specific packages
Stack All packages available (favors Stackage), root not required Installed in home directory, versions are pinned to snapshot, difficult to remove specific packages
AUR Additional packages available Risk of unmaintained or orphaned packages, incompatible versions of packages possible

Cabal

Note: In Haskell, the term Cabal can refer to either:
  • Cabal file format that describes Haskell packages and their dependencies;
  • Cabal library that works with Cabal file format;
  • cabal command-line tool (provided by cabal-install package) that uses Cabal library to build Haskell packages.
In the context of this article, Cabal usually means cabal command-line tool unless specified otherwise.

Cabal is "the original" build system for Haskell. Most of libraries and tools you can find on Hackage can be installed via Cabal.

Installing packages

To run user-wide executables installed by Cabal, ~/.cabal/bin must be added to the $PATH environment variable. This can be done by putting the following line in your shell configuration file, for instance ~/.bashrc for bash or ~/.zshrc for zsh:

export PATH="$HOME/.cabal/bin:$PATH"

Run the following command to install a Hackage package and all of its dependencies in a single step:

$ cabal install --ghc-options=-dynamic package
Note: As of October 2020 Cabal ignores ghc-options from ~/.cabal/config while building packages with build-type: Custom. Therefore, it is necessary to specify --ghc-options=-dynamic flag on the command line otherwise you might experience build errors in setup.hs like Could not find module ‘Prelude’ There are files missing in the ‘base-...’ package.

It is possible to install a package system-wide with the --global flag, but this is strongly discouraged. With the user-wide install, all files are kept in ~/.cabal and libraries are registered to ~/.ghc, offering the possibility to do a clean-up easily by simply removing these folders. With system-wide install, the files will be dispersed in the file system and difficult to manage.

You can also build and install a Haskell package from source. To do this, run the following command from the package directory:

$ cabal install --ghc-options=-dynamic

Each Cabal package should specify a list of its dependencies and their version constraints in the .cabal file according to the Package Versioning Policy (PVP). During the package installation, Cabal tries to find a set of dependencies that satisfies all the constraints. This process is called dependency resolution.

There are reasons why Stack exists; Cabal is known to generate a lot of friction with beginners. Most of the time dependency resolution works well but sometimes it fails. In this case you will need to figure out the cause of the problem and give Cabal some hints about how to resolve offending dependencies. For example, sometimes it is necessary to say cabal install --allow-newer --ghc-options=-dynamic package to allow Cabal to ignore package's PVP-dictated upper bounds on dependency versions, effectively installing package with newer dependencies than the package author has permitted. It gets hairier for less-well maintained packages; for another example, see this thread about installing Idris (another programming language, written in Haskell), where one had to use both --allow-newer and --constraint='haskeline < 0.8.0.0' command-line flags to get a successful compile.

Removing packages

There is no easy way to do it. Cabal does not have support for this functionality but there are external tools like cabal-store-gc.

If you want to reinstall the entire user-wide Haskell package system, remove ~/.cabal and ~/.ghc and start from scratch. This is often necessary when GHC is upgraded.

Stack

Stack is another tool to manage Haskell packages. It pursuits slightly different goals than Cabal, with a slightly different philosophy. It uses Cabal library under the hood and integrates with Hackage — but maintains its own repositories of packages (snapshots) on Stackage with the promise that snapshots are curated and include packages which work well together.

Installing packages

In its default configuration, Stack installs compiled executables to ~/.local/bin. Add this directory to the $PATH environment variable in your shell configuration file, for instance ~/.bashrc for bash or ~/.zshrc for zsh:

export PATH="$HOME/.local/bin:$PATH"

Run the following command to download, build and install a Stackage package:

stack install package
Note: As of October 2020 Stack ignores ghc-options from ~/.stack/config.yaml while building Setup.hs. If you experience build errors in Setup.hs like Could not find module ‘Prelude’ There are files missing in the ‘base-...’ package, try to install ghc-static package as a workaround.

You can also build and install a Haskell package from source by running the following command from the package directory:

stack install --resolver resolver

Note that you should specify the same resolver as one used for stack setup command.

Removing packages

Stack does not support the "uninstall" operation.

If you want to reinstall the entire user-wide Haskell package system, remove ~/.stack directory and start from scratch. This is often necessary when GHC is upgraded.

Alternate installations

ghcup

ghcup makes it easy to install specific versions of ghc and cabal-install and can also bootstrap a fresh Haskell developer environment from scratch. It can be installed through ghcup-hs-binAUR. It follows the UNIX philosophy of do one thing and do it well. Similar in scope to rustup, pyenv and jenv. See the usage section for an overview.

Nix

Tango-view-fullscreen.pngThis article or section needs expansion.Tango-view-fullscreen.png

Reason: I cannot offer a good enough overview, due to no experience with it (Discuss in Talk:Haskell#)

It seems that many Haskell developers appreciate Nix and use it in their projects.

Tips and tricks

Static linking

Note: In the context of this article, static linking does not mean generating completely static ELF binaries. Only Haskell code will be linked statically into a single ELF binary, which may be dynamically linked to other system libraries such as glibc.

To use static linking, one must, at minimum, install the static boot libraries through the ghc-static package. This would allow you to build projects that depend exclusively on the boot libraries as well as any library that is not installed through the haskell-* packages.

Unfortunately, if your project depends on any of the haskell-* packages that you have installed, Cabal does not take the absence of static libraries into account during dependency resolution. As a result, it will try to use the existing haskell-* package and then fail with linker errors when it realizes the static libraries are missing. Unlike ghc-static, there are no “haskell-*-static” packages to fix this problem.

To work around this problem, you can install ghc-pristineAUR, which wraps over an existing GHC installation to create a separate GHC distribution in /usr/share/ghc-pristine, with a package database that contains only boot libraries. Then, to build software with static linking, one simply needs to invoke the wrapped compiler /usr/share/ghc-pristine/bin/ghc. For Cabal, this amounts to the following configuration:

cabal configure --with-compiler=/usr/share/ghc-pristine/bin/ghc

This can be made permanent by editing ~/.cabal/config or the file $CABAL_CONFIG points to (it can be created using cabal user-config init). Be aware that you still need ghc-static.

Alternatives to ghc-pristine:

  • One could manually run cabal install --force-reinstalls to shadow the corresponding haskell-* packages. This can be tedious as you must explicitly enumerate all transitive dependencies that coincide with an existing haskell-* package.
  • Use a completely separate GHC distribution: download the official Linux binaries for GHC and cabal-install and unpack them somewhere else. This is in effect what ghc-pristine does, but ghc-pristine uses less disk space.

Building statically linked packages with Cabal (without using shared libraries)

This section explains how to install cabal-install but from Hackage instead of the official cabal-install package from the repositories. This cabal-install will build Haskell packages without using shared libraries unlike the official cabal-install which requires you to link dynamically.

In theory with any cabal-install you could choose between both methods, static and dynamic, for linking your Haskell code. In practice because in Arch some basic Haskell libraries (haskell-* packages) are provided as shared objects (.so files) and those libraries are globally registered in Cabal, it has trouble using the same libraries for static linking. To avoid linking errors, it's also especially important to not to mix statically and dynamically Haskell packages installed on the same system, as Cabal does not fetch a required package once it has been globally registered (check them with the command ghc-pkg list) in one of its forms and not the other (independently of the linking type of the package that it's needed).

For these reasons, you have to make sure that the only two related Haskell packages you have installed are ghc, the compiler, and ghc-static, the boot libraries on its static form, (not stack, cabal-helper[broken link: package not found], cabal-install and none of the haskell-* dynamic libraries available in the official repositories).

You can also use Stack as an alternative build tool for Haskell packages, which will link statically by default. But if you still want to use Cabal to build using static linking, follow the next steps of this section to install your own compiled cabal-install from Hackage. For this purpose we are going to use the tool Stack that will help us fetching all the dependencies required and building your own cabal-install.

First you have to install stack-staticAUR because you will use it to bootstrap the compilation of your own Cabal and you do not want to pull as dependencies those packages from the official Arch repositories containing the haskell-* dynamic libraries.

Then prepare Stack downloading the package index. Stack will be used with the only purpose of bootstrapping the compilation of your own cabal-install but using the already installed ghc compiler:

$ stack setup --system-ghc

Now install your own cabal-install:

$ stack install --system-ghc cabal-install

This newly installed cabal-install has been compiled without shared libraries and will not use them when building packages by default. Also, this cabal-install will use the installed ghc compiler.

Alternatively, you can also use ghcup to install an already compiled cabal-install binary and even a specific version of ghc compiler.

See also