I spent some time today getting my emacs config set up to learn Haskell, and ran into a few issues; I figured I'd go ahead and document the process here for everyone's enjoyment. We're going to install and configure Haskell mode, then add a few extensions that'll make learning Haskell fun and easy!
I'm currently running haskell-mode for emacs, with the hs-lint
plugin, Haskell support for FlyMake (which provides on-the-fly syntax checking from the Haskell compiler), and code autocompletion. The steps covered by this tutorial are:
- Installing Haskell
- Configuring Haskell-Mode for Emacs
- Installing Haskell-Mode Extensions (Flymake support,
hs-lint
and autocompletion)
Installing Haskell
Before any of this Emacs jazz, we have to get Haskell, of course. The easiest way to do is to download the Haskell Platform, a "Batteries Included" package of the Glasgow Haskell Compiler.
Emacs Haskell Mode
The Haskell Mode for Emacs page at the Haskell wiki is a nice place to start your explorations, and describes how to install haskell-mode. I found it easier to use ELPA, the Emacs Lisp Package Archive (install instructions here). If you're using the Emacs starter kit, you've already got ELPA.
Once elpa's all set, install haskell-mode
with the following:
- run
M-x package-list-packages
in Emacs - tap
i
byhaskell-mode
- hit
x
to the start the install.
Configuring emacs.el
Before we get into linting or any other customizations, Add the following to your emacs config (~/.emacs
, or ~/.emacs.d/init.el
, if you're using the Starter kit):
(add-hook 'haskell-mode-hook 'turn-on-haskell-doc-mode)
;; hslint on the command line only likes this indentation mode;
;; alternatives commented out below.
(add-hook 'haskell-mode-hook 'turn-on-haskell-indentation)
;;(add-hook 'haskell-mode-hook 'turn-on-haskell-indent)
;;(add-hook 'haskell-mode-hook 'turn-on-haskell-simple-indent)
;; Ignore compiled Haskell files in filename completions
(add-to-list 'completion-ignored-extensions ".hi")
At this point, you should be able to start using Haskell in emacs. Let's write our first function.
- Create a file called
test.hs
. - type
C-c C-z
to get to the Haskell REPL, supplied by inf-haskell mode
Now, add the following to test.hs
:
module Examples where
square :: Integral a => a -> a
square x = x * x
Then type C-c C-l
in that buffer to load the file's contents into the REPL. C-c C-z
over to the repl again and try it out:
*Main> square 10
100
*Main> square 34
1156
Nice.
Flymake
Next, we're going to add support for Flymake, the emacs syntax-checker. This is a piece of Emacs functionality that'll run our Haskell files through the compiler every few seconds and associate any warnings and errors with some specific line in our Haskell file.
While we're at it, we'll configure hs-lint
, which we can run periodically on our Haskell files for a list of hints as to how to structure code better. For example, running hs-lint
on a Haskell file containing times2 x = (*) 2 x
yields a buffer with:
hlint /Users/sritchie/test.hs
/Users/sritchie/test.hs:1:1: Error: Eta reduce
Found:
times2 x = (*) 2 x
Why not:
times2 = (*) 2
1 suggestion
Definitely helpful in the learning process.
Flymake depends on an external perl script, hslint
, to pass the contents of the current buffer into the Haskell compiler.
- Download
hslint
from this gist and place it somewhere on your path. - run
chmod a+x hslint
to give the file executable privileges
Add the following to .emacs
:
(defun flymake-haskell-init ()
"When flymake triggers, generates a tempfile containing the
contents of the current buffer, runs `hslint` on it, and
deletes file. Put this file path (and run `chmod a+x hslint`)
to enable hslint: https://gist.github.com/1241073"
(let* ((temp-file (flymake-init-create-temp-buffer-copy
'flymake-create-temp-inplace))
(local-file (file-relative-name
temp-file
(file-name-directory buffer-file-name))))
(list "hslint" (list local-file))))
(defun flymake-haskell-enable ()
"Enables flymake-mode for haskell, and sets <C-c d> as command
to show current error."
(when (and buffer-file-name
(file-writable-p
(file-name-directory buffer-file-name))
(file-writable-p buffer-file-name))
(local-set-key (kbd "C-c d") 'flymake-display-err-menu-for-current-line)
(flymake-mode t)))
;; Forces flymake to underline bad lines, instead of fully
;; highlighting them; remove this if you prefer full highlighting.
(custom-set-faces
'(flymake-errline ((((class color)) (:underline "red"))))
'(flymake-warnline ((((class color)) (:underline "yellow")))))
Haskell Extensions
Auto Complete Mode
Now, let's add autocompletion. Autocomplete mode is awesome; it provides IDE-like word tab completion of words based on info in open buffers, and some knowledge of the modes of the emacs buffer you're currently working in. In Haskell, we'll get autocompletion of every function we define, plus help with core language constructs. Head over to Auto Complete Mode to download the package, and install with the following:
- Download and unpack Autocomplete mode
- Open emacs, and run
M-x load-file
- Point the minibuffer to
<autocomplete-root>/etc/install.el
- Follow the remaining AC install instructions.
That should get you all set for the next step.
Linting!
- Download hs-lint.el and haskell-ac.el and place each file inside of
~/.emacs.d
. (hs-lint
is our linter, of course, andhaskell-ac.el
provides autocomplete mode with some knowledge of a few core Haskell constructs.
Add the following to your .emacs
file:
(require 'hs-lint) ;; https://gist.github.com/1241059
(require 'haskell-ac) ;; https://gist.github.com/1241063
(defun my-haskell-mode-hook ()
"hs-lint binding, plus autocompletion and paredit."
(local-set-key "\C-cl" 'hs-lint)
(setq ac-sources
(append '(ac-source-yasnippet
ac-source-abbrev
ac-source-words-in-buffer
my/ac-source-haskell)
ac-sources))
(dolist (x '(haskell literate-haskell))
(add-hook
(intern (concat (symbol-name x)
"-mode-hook"))
'turn-on-paredit)))
(eval-after-load 'haskell-mode
'(progn
(require 'flymake)
(push '("\\.l?hs\\'" flymake-haskell-init) flymake-allowed-file-name-masks)
(add-hook 'haskell-mode-hook 'flymake-haskell-enable)
(add-hook 'haskell-mode-hook 'my-haskell-mode-hook)))
The above code binds C-c l
to hs-lint
inside of Haskell buffers, and configures a large number of Haskell keywords for autocompletion. Go and test it out inside of test.hs
; you should find that square
autocompletes when you begin typing.
Now go ahead and add the following line to test.hs
:
face :: Int -> Bool
Upon save, or within seconds, you should see an angry red underline. Move the cursor over that line and type C-c d
, and you'll see a tooltip with the following text:
1: The type signature for `face' lacks an accompanying binding.
Adding this will clear things up:
face x = 5 < x
Finishing Up
I hope this helped those of you looking to get started exploring Haskell! Please let me know in the comments if anything could be clearer; I'll be posting more down the road, and all requests are welcome.
Comments
comments powered by Disqus