When it comes to programming environments, I am extremely fussy. Here is a list of features that I consider necessary for programming efficiently:
- Call Tips
- Hover-over Intellisense that presents all type info and comments attached to the identifier being queried.
- A debugger
Note that the aforementioned intellisense features should be based on static typing; I do not like editors that guess auto-completion items, call tips, or definition locations, because it is distracting when these guesses are wrong.
In summary, I am an IDE fan. Statically typed, IDE based programming is the only way that makes sense to me. When I worked in the game industry, Lua’s dynamic typing frustrated me so much that I desiged a static type system for it. Note, however, that I am only speaking for myself; I know other programmers who are far more talented and productive than I am and refuse to use anything other than emacs. I’m only making a point of this to identify the target audience of this post. If, like me, you are a fussy IDE user who is looking for a stable Haskell IDE, then read on.
The Search Begins
I recently started programming in Haskell, working with a moderate sized pre-existing project. My first task, unsurprisingly, was to set up a programming environment. IDE user that I am, I began by searching the internet for all projects which refer to themselves as Haskell IDEs. It turns out that none of these IDEs were able to function correctly for editing my pre-existing project.
Here is a list of IDEs that I tried.
- EclipseFP - This impressively comprehensive eclipse plugin froze when I tried opening my pre-existing project. It also seemed fairly unresponsive for smaller test projects.
- Leksah - Another nice IDE, but hover-over and go-to-definition did not seem to work.
- Sublime Haskell - I can’t remember what went wrong with this one.
- IntelliJ Plugin - There are several of these. I can’t remember which one I tried, but its parser could not deal with unicode.
Most of these IDEs, especially EclispeFP and Leksah, were very impressive. As someone who has developed open source IDEs in the past, I know that IDE development takes a huge amount of work, so I am sympathetic to unstable IDEs. If you’re interested in Haskell IDEs, consider trying these projects or even contributing to them! However, if you need a stable IDE for a medium-to-large project, I can’t recommend them.
Back To Emacs
At this point, I was getting desperate for a good Haskell environment. All the Haskell programmers that I know use either emacs or vim, so I decided to give emacs a try. The default Haskell mode for emacs fails my feature checklist miserably. I found its inability to query the types of local variables particularly worrying. Haskell is such a type rich language that I felt blindfolded trying to read it without the ability to query types.
Fortunately, there is another haskell mode for emacs. Unlike haskell mode, it does have the ability to query types. It also has several other nifty features and, most importantly of all, it is robust.
The emacs haskell mode that I am referring to is called ghc-mod. To install it, one must download two different packages. One is the hackage package named ghc-mod. The other is the MELPA package named ghc.
After adding the following to your .emacs file, ghc-mod should work.
(autoload 'ghc-init "ghc" nil t) (autoload 'ghc-debug "ghc" nil t) (add-hook 'haskell-mode-hook (lambda () (ghc-init))) (add-hook 'haskell-mode-hook 'turn-on-haskell-simple-indent)
There exists an online package archive, hackage, which contains documentation for nearly all published Haskell packages. A tool called Hoogle can be used to search hackage. Search keys can be keywords appearing in documentation, the names of exported values, most interestingly, the types of exported values.
Hoogle exists as both an web based search engine and a command line tool. Typing C-c C-h with ghc-mod installed will prompt the user for a hoogle search, using the identifier the caret is currently over as the default search key. By default, a web based search is opened in a browser. I personally find this distracting, however, preferring instead to use the hoogle command line tool.
The hoogle command line tool can be installed from the hackage package named hoogle. It is then executed as the hoogle command. A typical search can be performed by typing the command:
hoogle "search key"
While it isn’t strictly necessary to surround the search key in quotes, it becomes necessary when the search key contains special characters such as >. I don’t actually use the hoogle command directly, though; ghc-mod gives the hoogle command line tool precedence over web based search. If it finds hoogle in the current path, it will respond to C-c C-h queries by running the hoogle command, placing the result in an emacs buffer.
By default, the hoogle command line tool only searches Haskell’s Prelude standard library. If one wishes to search the documentation of an arbitrary hackage package foo, its documentation must first be downloaded. This is easily accomplished by typing
hoogle data foo
Subsequent hoogle searches which include foo as a search target must include +foo. For example, to search for the bar function in the foo package, we type
hoogle +foo "bar"
If we wish to include foo as a search target for searches triggered by ghc-mod,
we must edit the Haskell Hoogle Command customization item in emacs. This is accessed in emacs via Options->Customize Emacs->Top Level Customization groups;
searching for hoogle should yield the item in question. Its value is
hoogle, but if we are including foo as a search target,
we must change it to
hoogle +foo. Better yet, if we change it to
hoogle +foo --info, hoogle searches will produce not only types, but
descriptions as well.
A Haskell project will most likely depend on several external packages, all of which should be included in hoogle searches. Multiple packages can be included in hoogle searches by adding each of them to the hoogle command, prefixed with +:
hoogle +foo +bar +baz …
A more efficient approach, which I use, involves merging the hoogle databases for all of a project’s dependencies into a single hoogle database
In summary, hoogle is a great tool. And not only does the hoogle/ghc-mod combination allow me to avoid distracting web browser windows, it also allows me to program in Haskell without an internet connection. I find this an effective way to avoid distractions while programming.
Tags and Hasktags
ghc-mod does not provide any functionality equivalent to the go-to-definition feature. However, emacs provides some degree of built in go-to-definition functionality with its tags feature. A tags file contains a map from program identifiers to source code locations. If a tags file has been generated for a project, we can jump to the definition of an identifier by typing M-. and the entering the identifier in question into the resulting prompt.
To generate tags files for haskell projects, I have been using a tool called
hasktags. Hasktags is on hackage and can be installed via
cabal. Once the hasktags tool is in our path, we can generate tags for a Haskell
project by typing
hasktags -e project-root-directory, where project-root-directory is the root directory of the haskell project we wish to
generate tags for. Hasktags will recursively descend from the root directory,
generating tags for all .hs files that it encounters. The resulting tag set
will be placed in a file called TAGS. Place the TAGS file somewhere that
you will remember, such as the root directory of your project, because
the first time any tag command is executed during an emacs session, you
will be queried for the location of the tags file to read from.
There should be an emacs customization item called Haskell Tags on Save, which will update the tags file whenever one of the project’s .hs files is saved. Make sure that this item is set so that you won’t frequently have to run the hasktags command manually.
Hasktags isn’t perfect. It seems that it only takes me to the definition I am searching for about 85% of the time. In all other cases, I fall back on performing a text search of my project, which brings me to the next item.
Project Text Search with Grep
It would be nice to have the ability to search all source files of our project for code containing strings of our choosing. Emacs has built-in support for grep, which can handle this quite nicely. Typing M-x grep will prompt the user with a grep command. The output of the grep command will appear in a new emacs buffer, with colored text and clickable links.
However, the default grep command only searches the current directory rather than our entire project. To change the default grep command, we can alter emacs’s grep command customization item. (We can navigate to this by clicking Options->Customize Emacs->Top Level Customization Group and then typing grep into the search bar.) My project-centric grep command follows.
cd ~/project-root && grep -nH -r --include "*.hs" -e
Replace ~/project-root with the actual root directory of your project, and you should have a nice default search command for your project.
Managing Multiple Haskell Projects
Notice that we have been setting emacs customization items in a manner that targets a specific project. All of these customizations get saved into the .emacs file, so if we want to work with multiple Haskell projects, it may be helpful to save a separate .emacs file for each project. Any time we wish to work with a specific project, we first copy the .emacs file for that project into our home directory.
So there you have it. The best Haskell IDE that I could find turned out to be an emacs configuration integrating three external tools: ghc-mod, hoogle, and hasktags. When I started learning Haskell, I had trouble finding a good programming environment for it. Hopefully this blog post will prove helpful to others who find themselves in the same position.