Skip to content
Andy Morris edited this page Feb 16, 2016 · 20 revisions

ghc-mod: Making Haskell Even More Fun

Build Status

<project mission statement goes here>

ghc-mod is different things depending on who you are, so I have to ask: who are you?


case you of
  Haskell Tooling Developer -> Using ghc-mod as a library
  Haskell IDE Developer     -> Using ghc-mod as a backend program
  Haskell Developer         -> Using ghc-mod in a development environment

Using ghc-mod in a Development Environment

To use ghc-mod in your development environment of choice you need two things:

  • The ghc-mod program from the Cabal package of the same name
  • A ghc-mod frontend to integrate it into your development environment

Installing the ghc-mod Program

To install the ghc-mod program simply run the following command in your home directory:

cabal install ghc-mod

This will place the ghc-mod executable into ~/.cabal/bin which you might have to add to your PATH depending on the frontend you are going to use.

If you would rather use Stack see here: Installing the ghc-mod Program using Stack

Adding .cabal/bin to the PATH environment variable

How to do this varies with the platform you are using, see below.

GNU/Linux

On GNU/Linux the best way to do this is via .profile. This file gets sourced (shell terminology) before your desktop session is executed and thus any environment variables set in there will propagate down to everything run in your session.

By adding something like the following to ~/.profile you can extend PATH to include ~/.cabal/bin:

# set PATH so it includes Cabals's bin directory if it exists
if [ -d "$HOME/.cabal/bin" ] ; then
    PATH="$HOME/.cabal/bin:$PATH"
fi
Mac OS X (with root)

Unfortunately the beautiful .profile trick does not work on OSX since their desktop sessions do not source environment variables from any place that can be easily altered for a single user.

So we have to resort to a more crude method that requires super user priviledges, unless you'd rather not do that in which case see below.

You can add something like the following to /etc/launchd.conf:

setenv PATH /usr/bin:/bin:/usr/local/bin:<home directory>/.cabal/bin

Replace <home directory> with the output of running echo $HOME in the shell.

You'll have to reboot for this to take effect.

(See http://serverfault.com/a/277034)

Mac OS X (without root)

You can also add the PATH setting to all applications where you are likely to need it seperately. Usually this is going to be your $EDITOR and your $SHELL:

Add ghc-mod exe to Emacs Add ghc-mod exe to Vim Add ghc-mod exe to Shell

Emacs (OSX)

In Emacs you can add the following to your .emacs configuration Emacs Manual, The Emacs Initialization File:

(setenv "PATH" (concat (getenv "PATH") ":" (getenv "HOME") "/.cabal/bin"))
(setq exec-path (append exec-path '((concat (getenv "HOME") "/.cabal/bin"))))

Note that here you have to modify both the PATH environment variable and the regular exec-path variable. Emacs initializes exec-path with the contents of PATH on startup but doesn't sync them afterwards so when you use the systemwide PATH approach you don't need to bother with that.

Vim (OSX)

In your .vimrc, or in some file sourced from there, you can use let to add to your path:

let $PATH .= (":" . $HOME . "/.cabal/bin")
Shell (OSX)

Here you can do exactly what we do in the Linux case above but you have to be aware that .profile is only evaluated by the shell and not your desktop environment. So any environment variables you change in there will only apply to programs started in a shell.

(Note: inlined from above)

By adding something like the following to ~/.profile you can extend PATH to include ~/.cabal/bin:

# set PATH so it includes Cabals's bin directory if it exists
if [ -d "$HOME/.cabal/bin" ] ; then
    PATH="$HOME/.cabal/bin:$PATH"
fi

Installing the ghc-mod Program using Stack

There are two ways to install ghc-mod with Stack:

  • Global installation
  • Per project installation

The basic procedure is mostly the same for both but slightly different. The basic command looks like this:

stack --resolver=nightly install ghc-mod

Note: The --resolver=nightly is needed until ghc-mod lands in the next Stackage LTS release, as of December 2015 this is not the case yet.

Global installation

First a few words of caution: Since Stack manages and pins GHC versions per project you need to make sure the ghc-mod you compiled as part of a global installation is linked against the same version of GHC as the project otherwise you will most likely get errors looking like this: readPackageDb: inappropriate type.

Simply run the above command in your home directory. This will install ghc-mod into ~/.stack/snapshots/*/nightly-*/*/bin/ and also copy the executables to ~/.local/bin which you will want to add to your PATH.

Per project installation

(Note: This is untested)

Run the following command in your project directory:

stack --resolver=nightly install ghc-mod --no-copy-bins

This will install ghc-mod into <project directory>/.stack-work/install/*/nightly-*/*/bin/.

Doing this has the advantage that you build ghc-mod with whatever GHC version your project is using but your frontend either has to support finding ghc-mod in .stack-work/ or you have to play around with PATH.

Once Support switching GHC versions without recompiling ghc-mod, #615 is fixed this should not be required anymore.

(See also: https://git.gnu.io/snowdrift/snowdrift/blob/master/TEXTEDITORS.md)

Frontend specific installation instructions

Emacs (native frontend)

Using the stable version

The Emacs front-end is available from stable MELPA. The stable version should always be compatible with the latest version of ghc-mod from hackage which is not true for plain MELPA!

You can also use a git checkout of the ghc-mod source repository as your source for the Elisp code if you prefer.

With ELPA

To use stable MELPA add this to your .emacs:

(require 'package)
(add-to-list 'package-archives
	     '("melpa" . "http://melpa-stable.milkbox.net/packages/"))
(package-initialize)

(add-hook 'haskell-mode-hook (lambda () (ghc-init)))

(See also http://www.emacswiki.org/emacs/ELPA)

With this configuration you can install the Emacs front using:

M-x package-refresh-contents RET
M-x package-install RET ghc RET

Note: the ELPA package is called ghc, not ghc-mod

Without ELPA

Obtain the ghc-mod source somehow and then add this to your .emacs

(add-to-list 'load-path "<wherever>/ghc-mod/elisp")
(setq ghc-debug t) ; enable debug logging
(autoload 'ghc-init "ghc" nil t)
(autoload 'ghc-debug "ghc" nil t)
(add-hook 'haskell-mode-hook (lambda () (ghc-init)))

Make sure the source matches the version of the ghc-mod executables you installed otherwise you will run into all sorts of trouble.

You can check this by looking at the ghc-version variable:

M-x describe-variable RET ghc-version RET

This value should match the output of the following command:

ghc-mod --version

Vim (ghcmod-vim)

TODO

https://github.com/eagletmt/ghcmod-vim https://github.com/eagletmt/neco-ghc

Atom (ide-haskell+haskell-ghc-mod)

TODO

https://atom.io/packages/ide-haskell https://github.com/atom-haskell/ide-haskell

https://atom.io/packages/haskell-ghc-mod https://github.com/atom-haskell/haskell-ghc-mod

Using ghc-mod as a Backend Program

Directly using ghc-mod is while still supported for the time being discouraged. You should look into working with haskell-ide-engine instead.

The ghc-mod backend program is somewhat crusty and carries a lot of legacy baggage so going forward we would like to see frontends use haskell-ide-engine instead. There we're trying to get the design right from the beginning and fix the fragmentation of the Haskell Tooling Ecosystem along the way.

Using ghc-mod as a Library

Internally ghc-mod uses the Glasgow Haskell Compilers's API to implement most of it's functionality.

In order to provide a hassle free experience to users ghc-mod tries hard to automatically, and correctly, detect and if needed tweak the environment GHC needs. It also handles some of the more cumbersome parts of getting a working compiler session up and running.

This functionality can be very useful to all kinds of Haskell development tools therefore want to expose all the useful abstractions ghc-mod provides.

Right now the ghc-mod API is pretty messy a result major internal rewrites and reorganization coupled with too little time for cleanups over the course of almost 100 releases! We would like to make a cut during v6.0 or so and completely re-do the API but we need more input from downstream tool writers to do that properly, see Library API Redesign.

Right now tools like Alanz's The Haskell Refactorer (HaRe) and mote use this environment handling so they can concentrate on their core functionality instead of worrying about environments.

Most recently the haskell-ide-engine project has sprung up and if you're planning to write any kind of tool that needs editor integration eventually you should definetly look into that. haskell-ide-engine does uses ghc-mod at it's core so you'll want to be familliar with it either way.

API "documentation" is here: Hackage docs.

OLD STUFF

Nix & NixOS

ghc-mod works fine for users of Nix who follow a recent version of the package database such as the nixos-15.09 or nixos-unstable channel. Just include the package ghc-mod into your ghcWithPackages environment like any other library. The Nixpkgs Haskell User's Guide covers this subject in great detail.

Using the development version

The easiest way to hack on ghc-mod is compile it, then add dist/build/ghc-mod and dist/build/ghc-modi to your PATH and add the elisp/ directory to your Emacs load-path.

Make sure you're not using the MELPA version of ghc.el otherwise you might get all sorts of nasty conflicts.

Custom ghc-mod cradle

To customize the package databases used by ghc-mod, put a file called ghc-mod.package-db-stack beside the .cabal file with the following syntax:

temp directory root
package db 1
...
package db n

each package database line is either a path to a package database, or global or user.

IRC

If you have any problems, suggestions, comments swing by #ghc-mod (web client) on Freenode. If you're reporting a bug please also create an issue here so we have a way to contact you if you don't have time to stay.

Do hang around for a while if no one answers and repeat your question if you still haven't gotten any answer after a day or so. You're most likely to get an answer during the day in GMT+1.