Difference between revisions of "Haskell mode for Emacs"

From HaskellWiki
Jump to: navigation, search
(add note about speedbar/emacs support for haskell mode)
(Redirect to Emacs which will be part of a series of pages.)
(33 intermediate revisions by 10 users not shown)
Line 1: Line 1:
haskell-mode is a major mode for Emacs and XEmacs specifically for writing Haskell code. You can get haskell-mode from the web page: http://projects.haskell.org/haskellmode-emacs/ or on Debian you can type <code>apt-get install haskell-mode</code> (although this currently doesn't have the latest version). It is currently maintained by Svein Ove Aas[http://www.mail-archive.com/haskell-cafe@haskell.org/msg66300.html].
==Development Version==
$ darcs get http://code.haskell.org/haskellmode-emacs/
Download and unpack the basic mode and modules into a suitable directory, e.g. <code>~/lib/emacs/haskell-mode/</code> where <code>~</code> stands for your home directory.
Assuming you have placed the basic mode haskell-mode.el and the modules you want to use in the directory <code>~/lib/emacs/haskell-mode/</code>, add the following command to your init file (<code>~/.emacs</code>):
(load "~/lib/emacs/haskell-mode/haskell-site-file")
adding the following lines according to which modules you want to use:
(add-hook 'haskell-mode-hook 'turn-on-haskell-doc-mode)
(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)
Note that the three indentation modules are mutually exclusive - add at most one. Note that the line of code for simple indentation is commented out (using a preceding ;) in preference for the more advanced indentation module. Installation is now complete!
==== Inferior Mode ====
Normally, inf-haskell automatically finds ghci or hugs in your <code>PATH</code>, but if that's not the case (common under Windows), or if you need to specify your preference, just tell Emacs which executable to use with:
(setq haskell-program-name "/some/where/ghci.exe")
If you want to use different settings when you use Cygwin Emacs and NTEmacs,
you can test the value of `system-type':
(setq haskell-program-name
(if (eq system-type 'cygwin)
Note that Cygwin binaries tend to interact poorly with NTEmacs, especially
w.r.t signal-handling.
==Tips and use==
Handy keybindings in haskell-mode. See the documentation <code>C-h m</code> for more information:
*<code>C-c C-=</code> inserts an = sign and lines up type signatures and other pattern matches nicely.
*<code>C-c C-|</code> inserts a guard
*<code>C-c C-o</code> inserts a guard <hask>| otherwise =</hask> and lines up existing guards
*<code>C-c C-w</code> inserts a where keyword
*<code>C-c C-.</code> aligns code over a region in a "sensible" fashion.
Now in version 2.2:
*<code>C-c C-t</code> gets :type for symbol at point, and remembers it
*<code>C-u C-c C-t</code> inserts a type annotation, for symbol at point, on the line above
*<code>C-c C-i</code> gets :info for symbol at point
*<code>C-c M-.</code> find definition of (interpreted) symbol at point
(See the section below on [[#inf-haskell.el:_the_best_thing_since_the_breadknife|inf-haskell]].)
Here's an example for <code>C-c C-=</code>. Put your cursor after myInt and hit <code>C-c C-=</code>
blah :: Int -> Int
blah myInt
note how the function signature is reindented to match the column of the = sign.
blah :: Int -> Int
blah myInt =
You could also achieve the same effect by selecting the region and typing <code>C-c C-.</code>
You can also use Haskell-Mode to load Emacs buffers with Haskell code in either Hugs or GHC. To load something in Hugs or ghci, type <code>C-c C-l</code> to load the file. Then, you can go on to type <code>C-c C-r</code> (or simply <code>C-c C-l</code> again) to reload the current module when you have made a change.
=== Indentation ===
Indentation is one thing that nearly all programming modes provide. However, indenting Haskell code is very hard: for a given line, there are nearly always more than column for which indentation makes sense. For example, imagine the following is open in a haskell-mode buffer, where <code>!</code> represents the point:
foo :: Int -> String
foo 0 = f 4 ++ s
where f 4 = "hello" ++
If you ask haskell-mode to indent for you, where should it indent to? There are four basic options:
You want to finish off the expression you were writing in the last line. Haskell-mode indents to be underneath the <code>"</code> character at the beginning of <code>"hello"</code>:
where f 4 = "hello" ++
This is debatably a bad choice as you'd probably want to indent a bit further in to make it clear that you were carrying on an expression, but the layout rule would accept something like the following:
where f 4 = "hello" ++
You want to add a second equation for <code>f</code>. Haskell-mode will indent to line up with the first argument, and fill in the <code>f</code> in the equation:
where f 4 = "hello" ++
f !
This is an unlikely choice as the expression in the previous line isn't complete, but haskell-mode isn't smart enough to know that. (If <code>f</code> had been something without arguments, like <hask>where f = "hello"</hask>, then it's impossible to have more than one equation and haskell-mode won't offer this indentation level.)
You want to add a second binding to the <code>where</code>-block. Haskell-mode indents to line up with the <code>f</code>:
where f 4 = "hello" ++
<li>You want to start an entirely new top-level binding. Haskell-mode indents to the first column:
foo :: Int -> String
foo 0 = f 4 ++ s
where f 4 = "hello" ++
These four locations can be reached by repeatedly pressing <code>TAB</code>. This is what's known as the tab-cycle. The innermost location is offered first, then cycling progresses outwards. Although this may seem like an inefficient system (and it is indeed a shame that Haskell's design didn't result in an unambiguous indentation system), you do quickly get used to the tab-cycle and indenting Haskell code.
==== indent-region ====
Using indent-region is generally a bad idea on Haskell code, because it would need to know which of the tab-cycle stops you wish to choose for each line. The innermost one is chosen in each case, which often results in unusable code. Moral: just don't use indent-region with haskell-mode.
==== Unicodifying symbols (Pretty Lambda for Haskell-mode) ====
In Haskell code, you can end up using a lot of mathematical symbols. It is possible to hack the fontifying features of Emacs to change the ASCII textual representations of arrows and operators into the nice-looking real symbols, much like you could with TeX. The following code is a compilation of Emacs lisp code found on the Emacs wiki on the [http://www.emacswiki.org/cgi-bin/wiki/PrettyLambda#toc4 Pretty Lambda] page (that page also has examples of how to apply the general Unicode defuns to other languages):
HOWEVER: due to the symbols taking up less space, this has the unfortunate side effect of changing the indentation stops that the indent key offers. This will mean that your code may not look properly aligned to those who do not have this feature in their editor, or could even mean that your code means something different to how it looks. (It is possible to contrive an example that looks correct in emacs, but actually fails to compile). The following is left for interest, but probably should NOT be used.
Haskell-mode has included this feature for a long time now, so you probably just need to <code>(setq haskell-font-lock-symbols t)</code> in your .emacs to use this feature.
(defun unicode-symbol (name)
"Translate a symbolic name for a Unicode character -- e.g., LEFT-ARROW
or GREATER-THAN into an actual Unicode character code. "
(decode-char 'ucs (case name
('left-arrow 8592)
('up-arrow 8593)
('right-arrow 8594)
('down-arrow 8595)
('double-vertical-bar #X2551)
('equal #X003d)
('not-equal #X2260)
('identical #X2261)
('not-identical #X2262)
('less-than #X003c)
('greater-than #X003e)
('less-than-or-equal-to #X2264)
('greater-than-or-equal-to #X2265)
('logical-and #X2227)
('logical-or #X2228)
('logical-neg #X00AC)
('nil #X2205)
('horizontal-ellipsis #X2026)
('double-exclamation #X203C)
('prime #X2032)
('double-prime #X2033)
('for-all #X2200)
('there-exists #X2203)
('element-of #X2208)
('square-root #X221A)
('squared #X00B2)
('cubed #X00B3)
('lambda #X03BB)
('alpha #X03B1)
('beta #X03B2)
('gamma #X03B3)
('delta #X03B4))))
(defun substitute-pattern-with-unicode (pattern symbol)
"Add a font lock hook to replace the matched part of PATTERN with the
Unicode symbol SYMBOL looked up with UNICODE-SYMBOL."
nil `((,pattern
(0 (progn (compose-region (match-beginning 1) (match-end 1)
,(unicode-symbol symbol)
(defun substitute-patterns-with-unicode (patterns)
(mapcar #'(lambda (x)
(substitute-pattern-with-unicode (car x)
(cdr x)))
(defun haskell-unicode ()
(list (cons "\\(<-\\)" 'left-arrow)
(cons "\\(->\\)" 'right-arrow)
(cons "\\(==\\)" 'identical)
(cons "\\(/=\\)" 'not-identical)
(cons "\\(()\\)" 'nil)
(cons "\\<\\(sqrt\\)\\>" 'square-root)
(cons "\\(&&\\)" 'logical-and)
(cons "\\(||\\)" 'logical-or)
(cons "\\<\\(not\\)\\>" 'logical-neg)
(cons "\\(>\\)\\[^=\\]" 'greater-than)
(cons "\\(<\\)\\[^=\\]" 'less-than)
(cons "\\(>=\\)" 'greater-than-or-equal-to)
(cons "\\(<=\\)" 'less-than-or-equal-to)
(cons "\\<\\(alpha\\)\\>" 'alpha)
(cons "\\<\\(beta\\)\\>" 'beta)
(cons "\\<\\(gamma\\)\\>" 'gamma)
(cons "\\<\\(delta\\)\\>" 'delta)
(cons "\\(<nowiki>''</nowiki>\\)" 'double-prime)
(cons "\\('\\)" 'prime)
(cons "\\(!!\\)" 'double-exclamation)
(cons "\\(\\.\\.\\)" 'horizontal-ellipsis))))
(add-hook 'haskell-mode-hook 'haskell-unicode)</code>
== Bugs ==
Bugs and feature requests should be sent to the maintainer [mailto:svein.ove@aas.no Svein Ove Aas].
For people using the Debian package, Debian maintains a [http://bugs.debian.org/cgi-bin/pkgreport.cgi?pkg=haskell-mode list of bugs] for haskell-mode, they should be reported there.
On some the GNU/Linux systems with XEmacs, admittedly, only verified on Ubuntu and Debian and with haskell-mode 2.2, there is a system function missing that interferes with automatic indenting. Secondly, there seems to be an issue with setting the <code-lisp>haskell-default-face</code-lisp> to <code-lisp>nil</code-lisp>.
To fix this, find where the haskell mode package is installed on your system. (Usually <code>/usr/share/emacs/site-lisp/haskell-mode</code>). Edit the file <code>haskell-indent.el</code> and add the lines:
;; If `line-end-position' isn't available provide one.
(unless (fboundp 'line-end-position)
(defun line-end-position (&optional n)
"Return the `point' of the end of the current line."
(end-of-line n)
right after the comments at the top. That should fix the issue.
This one shows up when typing in code (at various spots - most often when typing a qualified function, such as <hask>List.map</hask>.)
To fix this one, edit the file <code>haskell-font-lock.el</code>. Look for the line that says:
(defvar haskell-default-face nil)
and change this to
(defvar haskell-default-face 'default)
In my version, this is line 168.
Then, look for the line that says:
(,qvarid 0 haskell-default-face)
and change it to
(,qvarid 0 (symbol-value 'haskell-default-face))
For me, this is line 326 of the file.
YMMV - hope this helps.
=== GNU Emacs ===
==== ghci buffer infested with "^J"s, C-c C-t doesn't work ====
(only happens after loading a haskell file into ghci)
== inf-haskell.el: the best thing since the breadknife ==
inf-haskell.el is _awesome_. At one point I decided to sit down and write a list of functions I'd love to have in haskell-mode, intending to write them myself. I thought I'd check to see whether the key shortcuts I'd chosen were free but I was surprised to find that ''every one'' of these functions is already provided by inf-haskell.el! Here's a selection of the highlights:
=== Getting set up ===
inf-haskell.el is usually already setup as part of the haskell-mode package, so there is nothing special to do for it. On some systems, you may need this in your .emacs:
(require 'inf-haskell)
To use the following functions, first find a .hs file, then hit C-c C-l (inferior-haskell-load-file). This fires up Hugs or Ghci (you can change this by customising haskell-program-name) on your file. Don't worry if it's not an isolated module, GHCi will load all the modules it imports as normal. You can even load entire programs this way by using C-c C-l on the Main.hs file. If everything loads without errors, you'll be able to use the functions below.
=== inferior-haskell-type (C-c C-t) ===
Say you have the following code:
foo = foldr (+) 0 [1..20]
Perhaps you've forgotten the order of arguments to foldr. It's easily done; I can never remember whether the operation or final value comes first. That's easy to check: just put your point between the 'f' and 'r' of 'foldr' and hit C-c C-t RET. The type of foldr will be revealed in the echo area. This isn't particularly impressive; haskell-doc.el already did this. However, this will work for ''any'' function in the module in question ''or'' in those modules imported by the current module (including the standard libs)!
If you find that the type shown in the echo area is overwritten after a short amount of time (or any other such problem, of course), please report it as a bug. We know of no such bug, but someone apparently bumped into some such problem which he says he worked around by disabling doc-mode and decl-scan:
To turn off haskell-doc-mode, add the following to your .emacs:
(remove-hook 'haskell-mode-hook 'turn-on-haskell-doc-mode)
To turn off haskell-decl-scan, just refrain from turning it on (it's not enabled by default).
(P.S. I re-use haskell-doc-mode to save queried type info, and re-display it in the minibuffer. Disabling doc mode would disable that. -- mrd)
Another nice feature of this function is the ability to automatically insert type signatures for the function at point on the line above. For example, suppose you have the below open in Emacs, with the point represented by -!-:
-!-map _ [] = []
map f (x:xs) = f x : map f xs
And press C-u C-c C-t (note the prefix argument), it will result in the following:
map :: (a -> b) -> [a] -> [b]
-!-map _ [] = []
map f (x:xs) = f x : map f xs
=== inferior-haskell-info (C-c C-i) ===
The :info command in GHCi/Hugs is extremely useful; it'll tell you:
* The definition of an algebraic datatype given its name. E.g. try <code>:info Bool</code>. The output will contain something like <code>data Bool = True | False</code>.
* The classes a type instantiates given the type's name. <code>:info Bool</code> will also give you the classes Bool instantiates. If you can't see an instance you think should be there, make sure the module where that instance is declared is loaded.
* The type of a function, given its name.
* The types of the methods of a class, and the number of arguments of that class, given the class name.
* The expansion of a type synonym given that synonym's name.
And for all of the above, :info will also tell you the filename and line where that thing is defined. inferior-haskell-info lets you hook into this power. Use it with C-c C-i on anything within a Haskell file.
=== inferior-haskell-find-definition (C-c M-.) ===
This one needs little explanation. Sometimes you just need to find the source of a function, or datatype, or class, or type synonym etc. to see how it works, and this function lets you do just that. Unfortunately, it won't work on the standard lib modules or anything that isn't 'local' to your project. This is one of the most useful functions inf-haskell.el provides.
(Basically, it only works on interpreted code, for which ghci has location information. If you want a more general find-definition, use hasktags to create a TAGS file and then use the normal emacs <code>M-.</code> with that. -- mrd)
: Note that you can also create a TAGS file using GHCi's :etags command. [[User:DavidHouse|DavidHouse]] 14:38, 29 April 2007 (UTC)
: Again, :etags/:ctags only works for interpreted code.
: <code>inferior-haskell-mode</code> is missing TAB completion, which in GHCi works basically for everything (GHCi commands, modules, functions, language extensions, file names etc.). -- Oleksandr Manzyuk
== Tricks and tweaks ==
=== Automatic unit testing ===
Here's a cute trick I've evolved:
I'm a great fan of [[unit test first]], as described by eXtremeProgramming on TheOriginalWiki.
With the code below, I can press F12 and immediately run all of my unit tests, and immediately see whether they all passed or not.
I've put all of my unit tests into their own file with a main function that runs the tests and gives an exitcode according to the test results. I've specified that the compile-command for that file compiles and runs the file.
This elisp code will run the <code>compile</code> command from the F12 key in emacs. The output will popup a new window twelve lines tall. If the compilation is successful (exitcode zero) the window goes away. If the exitcode is 1 or greater, the window stays so you can see the output.
(require 'compile)
;; this means hitting the compile button always saves the buffer
;; having to separately hit C-x C-s is a waste of time
(setq mode-compile-always-save-buffer-p t)
;; make the compile window stick at 12 lines tall
(setq compilation-window-height 12)
;; from enberg on #emacs
;; if the compilation has a zero exit code,
;; the windows disappears after two seconds
;; otherwise it stays
(setq compilation-finish-function
(lambda (buf str)
(unless (string-match "exited abnormally" str)
;;no errors, make the compilation window go away in a few seconds
"2 sec" nil 'delete-windows-on
(get-buffer-create "*compilation*"))
(message "No Compilation Errors!"))))
;; one-button testing, tada!
(global-set-key [f12] 'compile)
This Haskell code has some Emacs local variable settings at the bottom specifying what the compile-command should be for this buffer.
import HUnit
import System
myTestList =
TestList [
"add numbers" ~: 5 ~=? (3 + 2)
,"add numbers" ~: 5 ~=? (3 + 3)
h = runTestTT myTestList
main = do c <- h
putStr $ show c
let errs = errors c
fails = failures c
System.exitWith (codeGet errs fails)
codeGet errs fails
| fails > 0 = ExitFailure 2
| errs > 0 = ExitFailure 1
| otherwise = ExitSuccess
-- Local Variables:
-- compile-command: "ghc --make -o Test_Demo -i/home/shae/src/haskell/libraries/ HUnitDemo.hs && ./Test_Demo"
-- End:
If you have any questions, ideas, or suggestions for this code, the maintainer would love to hear them.
=== Hoogle integration ===
From haskell-mode version 2.4 onwards, the built-in function haskell-hoogle will hoogle the identifier at point.
=== Using rectangular region commands ===
Emacs has a set of commands which operate on the region as if it were rectangular. This turns out to be extremely useful when dealing with whitespace sensitive languages.
<code>C-x r o</code> is "Open Rectangle". It will shift any text within the rectangle to the right side. Also see:
<code>C-x r t</code> is "String Rectangle". It will shift any text within the rectangle over to the right, and insert a given string prefixing all the lines in the region. If comment-region didn't already exist, you could use this instead, for example.
<code>C-x r d</code> is "Delete Rectangle". It will delete the contents of the rectangle and move anything on the right over.
<code>C-x r r</code> is "Copy Rectangle to Register". It will prompt you for a register number so it can save it for later.
<code>C-x r g</code> is "Insert register". This will insert the contents of the given register, overwriting whatever happens to be within the target rectangle. (So make room)
<code>C-x r k</code> is "Kill rectangle". Delete rectangle and save contents for:
<code>C-x r y</code> is "Yank rectangle". This will insert the contents of
the last killed rectangle.
As with all Emacs modifier combos, you can type <code>C-x r C-h</code> to find out what keys are bound beginning with the <code>C-x r</code> prefix.
=== Aligning code ===
Emacs22 has a neat tool called: align-regexp. Select a region you want to align text within, M-x align-regexp, and type a regexp representing the alignment delimiter.
For example, I often line up my Haddock comments:
f :: a -- ^ does a
-> Foo b -- ^ and b
-> c -- ^ to c
Select the region, and let the regexp be: <code>--</code>
f :: a -- ^ does a
-> Foo b -- ^ and b
-> c -- ^ to c
Of course, this works for just about anything. Personally, I've globally bound it to <code>C-x a r</code>:
<code>(global-set-key (kbd "C-x a r") 'align-regexp)</code>.
=== Automatically building ===
Emacs 21 has a package that can be installed (included by default in 22 and up) called 'FlyMake'; the idea is that as you are editing away, it occasionally calls the interpreter/compiler automatically and keeps track of whether the code works or not. You can fairly easily get it to work for Haskell as well; see [http://www.emacswiki.org/cgi-bin/wiki/FlymakeHaskell FlymakeHaskell] on the Emacs wiki.
=== Emacs Integration with Hayoo ===
My newly installed system would not allow me to hoogle what I wanted (no xmonad or xmonadcontrib in hoogle) so someone suggested Hayoo.
(define-key haskell-mode-map (kbd "<f3>") (lambda()(interactive)(browse-url (format "http://holumbus.fh-wedel.de/hayoo/hayoo.html?query=%s&start" (region-or-word-at-point)))))
region-or-word-at-point is available in the thing-at-pt+.el library.
Added 22-12-2008 - Promt for hayoo word
(defun rgr/hayoo()
(let* ((default (region-or-word-at-point))
(term (read-string (format "Hayoo for the following phrase (%s): "
(let ((term (if (zerop(length term)) default term)))
(browse-url (format "http://holumbus.fh-wedel.de/hayoo/hayoo.html?query=%s&start" term)))))
(define-key haskell-mode-map (kbd "<f3>") (lambda()(interactive)(rgr/hayoo)))
Alteratively use the excellent browse-apropos-url stuff:
[http://richardriley.net Richard]
Note: Using and URL like this should work too and will give better results (not yet tested as I'm not an emacs user):
[[User:Tbh|Tbh]] 00:56, 25 January 2009 (UTC)
=== Integration with HLint ===
[http://www-users.cs.york.ac.uk/~ndm/hlint/ HLint] could be used directly from Emacs buffer. There is [http://www.cs.york.ac.uk/fp/darcs/hlint/data/hs-lint.el hs-lint.el] package (in HLint distribution, in the data subdirectory), that allows user to run HLint and navigate through errors, or apply suggestions to the source code.
This package implements `hs-lint` command, that behaves differently depending on settings. If the `hs-lint-replace-with-suggestions` variable is set to `t`, then it will ask user and apply concrete suggested change to source code, otherwise you'll be able to navigate through list of suggestions with `next-error` command. If the `hs-lint-replace-without-ask` variable is also set to `t`, then all suggestions will applied automatically.
You can also bind `hs-lint` command to some key (in this example this is `C-c l`) with following code:
(defun my-haskell-mode-hook ()
(local-set-key "\C-cl" 'hs-lint))
(add-hook 'haskell-mode-hook 'my-haskell-mode-hook)
All settings, described above are available for customization via `hs-lint` customization group.
[[Category:Development tools]]
=== Folding ===
For folding parts of code you can use
*and '''hs-outline-level''' (based on py-outline-level created by Gb)
;; this gets called by outline to deteremine the level. Just use the length of the whitespace
(defun hsk-outline-level ()
(let (buffer-invisibility-spec)
(skip-chars-forward "\t ")
;; this get called after haskell mode is enabled
'(lambda ()
;; outline uses this regexp to find headers. I match lines with no indent and indented
;; some lines, such as "--" ... "class"
(setq outline-regexp "^[^\t ].*\\|^.*[\t ]+\\(where\\|of\\|do\\|in\\|if\\|then\\|else\\|let\\|module\\|import\\|deriving\\|instance\\|class\\)[\t\n ]")
;; enable our level computation
(setq outline-level 'hsk-outline-level)
;; do not use their \C-c@ prefix, too hard to type. Note this overides some python mode bindings
;;(setq outline-minor-mode-prefix "C-c")
;; turn on outline mode
(outline-minor-mode t)
;; initially hide all but the headers
*Also, you can use '''toggle-selective-display''' for global folding
;; folding for all rows, starting on the current column
(defun toggle-selective-display (column)
(interactive "P")
(or column
(unless selective-display
(1+ (current-column))))))
(global-set-key (kbd "C-x $") 'toggle-selective-display)
(defun toggle-hiding (column)
(interactive "P")
(if hs-minor-mode
(if (condition-case nil
(error t))
(toggle-selective-display column)))
(global-set-key (kbd "C-@") 'toggle-hiding)
*and '''narrowing capabilities''' for folding the rest of code
(put 'narrow-to-defun 'disabled nil)
(put 'narrow-to-page 'disabled nil)
(put 'narrow-to-region 'disabled nil)
*or '''folding-mode''', if you do not mind add markers such as '''{-{{{-}''' and '''{-}}}-}''' in code.
=== Speedbar ===
Emacs has a [http://www.emacswiki.org/emacs/SpeedBar SpeedBar] which works quite nice together with haskell-mode and also detects syntactic elements such as variables, imports, instances, classes.
Just add
(require 'speedbar)
(speedbar-add-supported-extension ".hs")
to your .emacs file and fire up speedbar with <code>M-x speedbar</code>.

Latest revision as of 10:01, 18 May 2012

Redirect to: