Haskell - installation

Pre-requisities

  1. Serious Haskell projects (not textbook problems) are memory-guzzlers during development and compilation.
  2. So, it won’t be long before your system starts swapping, which is why it’s better to have an SSD along with a lot of RAM.
  3. For serious Haskell development, we recommend at least 10 GB of RAM and at least 256 GB SSD.
  4. For text-book practice, you can probably get away with 4GB of RAM and an HDD.

Installing Haskell

There are a few different ways to install Haskell! Here’s what they are:

  1. Use https://www.haskell.org/ghcup/ (This seems to be a prerequisite for VSCode Haskell Language Support plugin. So, might as well use this for installation.) After installing,
    1. To start a simple repl, run: ghci
    2. To start a new haskell project in the current directory, run: cabal init --interactive
    3. To install other GHC versions and tools, run: ghcup tui
  2. Via Stack - https://haskell-lang.org/get-started (recommended) Out of all the methods listed, only Stack is recommended for maintaining your sanity. So, head over to Stack’s Get Started page and follow only the first step, titled “Download Haskell Stack” for your OS. The other steps given on that page are covered in greater detail below.
  3. Via your OS’ native package manager, i.e. apt-get, homebrew, yum, pacman
  4. Via a minimal installer or the Haskell Platform

Quick primer on Stack

stack --version

stack solves the following problems:

  1. Having different versions of the Haskell compiler (i.e. ghc) available on your machine without messing things up, and using the right ghc version for your project.
  2. Taking care of which Haskell libraries are known to compile/build with which version of ghc.
  3. Taking care of the dependency graph of libraries, so that all the libraries that your project depends on, compile successfully without you having to manually specify the version of each library. Basically stack saves you from the dependency hell problem.
  4. In a sense stack is similar to the following tools from other ecosystems, which attempt to solve some, or all, of the same problems (but they may have solved them in a different manner):
    1. rvm and bundler from the Ruby world
    2. virtualenv from the Python world
    3. gvm from the Go world
    4. nvm or yarn from the NodeJS world

Setup your first throw-away project with stack

$ stack new first-project (if you do not want to specify a resolver)

or

$ stack new --resolver=lts-9.14 first-project
$ cd first-project

Use either stack setup or stack build:

$ stack setup
  1. The command stack setup may take forever, because it will probably download the GHC compiler and a bunch of core/base libraries.
  2. Also, it’s going to take a lot of disk-space (about 2GB+).
  3. The stack setup will download the compiler if necessary in an isolated location (default ~/.stack) that won’t interfere with any system-level installations.
  4. For information on installation paths, please use the stack path command.
  5. You might have a global ghc pkg list that has incorrect versions of packages and stack might try to use it and this will result in problems. In order to verify this, call ghc-pkg list and see what shows up. Ideally, we should not run into this issue if we do stack setup and update the dependencies in the correct location in package.yaml

OK, before we go too much further, let’s just make sure this fresh project builds. Install dependencies and build project.

$ stack build

Pretty straight forward stuff! If everything’s been set up right, we should expect stack exec <project>-exe (corresponding to the executable target in the cabal file) to run the program as it sits now, as described by the Stack quickstart guide to write “someFunc” as output to STDOUT and exit immediately. Let’s make sure:

$ stack exec <insert-project-name-here>
$ stack exec first-project
someFunc

If you are choosing cabal instead of stack for dependency management, use these commands: cabal update, cabal install, cabal run

Creating custom hs files in your project and working with them

Create a new file called Throwaway.hs in your project:

Open first-project/src/Throwaway.hs and make sure it has the following contents:

module Throwaway where

addNumbers :: Int
addNumbers = 1 + 2

Now, fire-up GHCi:

$ stack ghci

Main Lib> :l Throwaway
Main> addNumbers
3

Now, make some changes to Throwaway.hs, WITHOUT EXITING GHCi:

module Throwaway where

addNumbers :: Int
addNumbers = 10 + 20
Next, reload these changes in GHCI:

Main> :r
Main> addNumbers
30

That’s the most basic development workflow to follow:

Always start ghci with the command stack ghci from within your project directory. This will ensure that the correct version of the compiler is used and that your GHCi is aware of the files in your project and the packages that your project depends on.

Load a file in GHCi via :l Run a function Change something in the function Reload the file via :r (There is a difference in the behaviour of :l and :r that you may read about, if you are interested.) Re-run the function

Questions about the project structure?

https://docs.haskellstack.org/en/stable/stack_yaml_vs_cabal_package_file/

During regular use you will find yourself updating package.yaml for the most part (stack.yaml when you need even more power than normal).

Make sure you are reading the correct docs

You will find yourself referring to API documentation very often. However, do not search for the package names on Google and start reading docs from there. You will end-up in a world of pain without even realising it. Google doesn’t know which version of the package you’re using, and from a first-hand experience, things change a lot between versions.

So, here’s what you should do:

  1. Check which LTS/resolver you are on – it’ll be the very first setting in your project’s stack.yaml file.
  2. Go to the Stackage homepage (https://www.stackage.org/) and find the listing page for your LTS/resolver. For example, here’s the listing for lts-9.17 (https://www.stackage.org/lts-9.17)
  3. Search for documentation and packages only from this page

Hackage vs Stackage

Strangely, Haskell has two widely used package repositories. Here is how they are conceptually different and why both exist:

Hackage (http://hackage.haskell.org/) is the original package repository. This is where authors upload their packages to share them with the world.

Stackage (https://www.stackage.org/) pulls specific versions of specific packages from Hackage and divides them into “sets” known as “snapshots”. Each snapshot contains only a single version of any package with a guarantee that all the packages in a given snapshot will successfully build when included in a project. That is, you will not get a dependency hell when your project depends on 5 packages from the same Stackage snapshot. (If you go to a snapshot/LTS’ listing page you can verify that there is only one version of each package listed there. On the other hand, if you check the same package on Hackage, you will see all versions of the package listed there).

Hackage has way more packages than Stackage, because, not every author adds their package to the Stackage snapshot. This is probably because, every time a new LTS/snapshot is released, package-authors have to do some extra work to maintain the “no dependeny-hell guaranteee”. However, most popular/important packages have already been added to Stackage, so you won’t be missing any packages till you start doing advanced Haskell stuff.

Cabal vs Stack

The command-line tool called cabal does not know about Stackage and pulls packages directly from Hackage. Also, cabal+Hackage do not give this “no dependency-hell guarantee” that stack+Stackage works very hard to maintain.

The command-line tool called stack pulls packages from Stackage, by default. Having said that, it can pull packages from Hackage if a particular package is not available in its snapshot (but this requires a few extra lines of config).

Finally, lot of cabal/Hackage lovers hate stack/Stackage and vice-versa. If you are in the mood for some gossip you can search the interwebs and read some flamewars. One hopes, that at some point in the future, the best parts of stack/Stackage and cabal/Hackage can be merged to build a unified, kickass build-tool. Till that day comes, just use Stack.

https://www.fpcomplete.com/blog/why-is-stack-not-cabal/

https://stackoverflow.com/questions/30913145/what-is-the-difference-between-cabal-and-stack


Links to this note