[Published in Open Source For You (OSFY) magazine, August 2013 edition.]
Emacs is a popular text editor that can be extended and customized. Haskell is a statically typed, functional programming language. Haskell-mode is a Emacs major mode that provides support to write and use Haskell programs. This article explains interesting features and tips on using Haskell-mode with GNU Emacs.
You can install Haskell-mode using your distribution package manager. For example, on Fedora you can use:
$ sudo yum install emacs-haskell-mode
Mode
You can enter Haskell-mode when opening a Haskell source file that has an extension .hs, or it can be started within Emacs using:
M-x haskell-mode
On the modeline, you will now see “(Haskell)”, indicating that the Haskell mode has been activated. You can enter the indent mode using:
M-x haskell-indent-mode
The modeline will now show “(Haskell Ind)”.
Interpreter
To load a Haskell source file into the interpreter, use C-c C-l. It will create a new buffer, load the module in the current buffer and give a prompt to work with. Consider the following Square.hs program:
square :: Int -> int
square x = x * x
Opening a Square.hs file in an Emacs buffer, and running C-c C-l will produce the following in a new buffer:
GHCi, version 7.0.4: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Prelude> :load "/home/guest/Square.hs"
[1 of 1] Compiling Main ( /home/guest/Square.hs, interpreted )
Ok, modules loaded: Main.
*Main>
If you have multiple buffers opened within Emacs, you can directly switch from the (Haskell) mode buffer to the Haskell interpreter using C-c C-z.
Insertions
The equal to (=) sign can be inserted, and the function type can be neatly aligned with the C-c C-= key stroke. If you type the following function:
volume :: Int -> Int -> Int
volume x
… and keep the cursor after ‘x’ and type C-c C-=, the equal to sign is inserted, and the code gets neatly aligned:
volume :: Int -> Int -> Int
volume x =
In the following code snippet, after ‘y’, if you hit Return followed by C-c C-|, a guard symbol is inserted:
max :: (Ord a) => a -> a -> a
max x y
|
After inserting the second guard in the above example, the ‘otherwise’ keyword can be inserted and the code is aligned using C-c C-o:
max :: (Ord a) => a -> a -> a
max x y
| x > y = x
| otherwise =
The ‘where’ clause is produced using C-c C-w. In the following example, pressing return after ‘r’, and using C-c C-w inserts the ‘where’ clause:
circleArea :: Float -> Float
circleArea r = pi * r * r
where
You can insert the type annotation for a function using C-u C-c C-t. Consider the sphereVolume function:
sphereVolume r = 4 / 3 * pi * r * r * r
where pi = 3.1412
Placing the cursor on ‘sphereVolume’ and typing C-u C-c C-t produces the following:
sphereVolume :: Fractional a => a -> a
sphereVolume r = 4 / 3 * pi * r * r * r
where pi = 3.1412
Formatting
There are a number of shortcut commands that are useful for indentation. Let’s suppose you have the following function with the cursor position indicated by ‘_’:
greeting :: String -> String
greeting x = "Hello" ++ x ++
_
Hitting TAB will take you through the different possible positions for inserting code. When you press TAB for the first time, the cursor will move under ‘Hello’; if you wish to complete the string concatenation (++), issue the following code:
greeting :: String -> String
greeting x = "Hello" ++ x ++
_
Hitting TAB again prepends ‘greeting’ and the cursor will be placed under ‘x’ for you to add another test condition, as follows:
greeting :: String -> String
greeting x = "Hello" ++ x ++
greeting _
Hitting TAB again will move the cursor to the first column if you want to add any text:
greeting :: String -> String
greeting x = "Hello" ++ x ++
_
As you keep hitting TAB again and again, the above sequence will repeat. Comments in Haskell begin with ’- -’.
one -- 1
two -- 2
three -- 3
four -- 4
five -- 5
six -- 6
seven -- 7
After marking the above region, use M-x align-regexp followed by ’–’ for the regexp, and the comments will be aligned:
one -- 1
two -- 2
three -- 3
four -- 4
five -- 5
six -- 6
seven -- 7
C-c C-. helps align the code neatly. Consider the Area.hs program:
area :: Int -> Int -> Int
area breadth height = breadth * height
After marking the above program, and using C-c C-., the code becomes:
area :: Int -> Int -> Int
area breadth height = breadth * height
Query
To know the Haskell-mode version, use M-x haskell-version. As an example:
Using haskell-mode version v2.8.0
C-c C-i on a symbol will prompt for getting information about the symbol. For example, ‘Show info of (default Int):’ lists the following:
data Int = GHC.Types.I# GHC.Prim.Int# -- Defined in GHC.Types
instance Bounded Int -- Defined in GHC.Enum
instance Enum Int -- Defined in GHC.Enum
instance Eq Int -- Defined in GHC.Base
instance Integral Int -- Defined in GHC.Real
instance Num Int -- Defined in GHC.Num
instance Ord Int -- Defined in GHC.Base
instance Read Int -- Defined in GHC.Read
instance Real Int -- Defined in GHC.Real
instance Show Int -- Defined in GHC.Show
C-c C-t will obtain the type of the symbol with the prompt ‘Show type of (default pi):’. For example:
pi :: Floating a => a
C-c TAB on a symbol returns its definition at the interpreter prompt, as follows:
*Main> :info sphereVolume
sphereVolume :: Fractional a => a -> a
-- Defined at /home/guest/Sphere.hs:1:1-12
To find haddock information for a symbol, you can use C-c C-d. Searching for ‘Float’, for example, opens up file:///usr/share/doc/ghc/html/libraries/ghc-prim-0.2.0.0/GHC-Types.html on Fedora.
To use the Hayoo search engine, you can use M-x haskell-hayoo. It will prompt with:
Hayoo query:
The query responses are shown in a browser. Similarily, the Hoogle engine can be queried using M-x haskell-hoogle. If you searched for ‘show’, it will open the URL http://www.haskell.org/hoogle/?q=show with the search results.
Files ending with .lhs are literate Haskell programs. You can use Richard Bird style to separate text and code as follows:
Insert blank line before the code
> quicksort :: Ord a => [a] -> [a]
> quicksort [] = []
> quicksort (p:xs) = (quicksort lesser) ++ [p] ++ (quicksort greater)
> where
> lesser = filter (< p) xs
> greater = filter (>= p) xs
Insert blank line after the code
The modeline will indicate that you are in the ‘(LitHaskell/bird)’ minor mode.
The hasktag package needs to be installed to help generate TAGS file for source files. For example:
$ hasktags Test.hs
It will create both tags and TAGS files. You can use M-. in the Haskell buffer to search for a tag.
Checks
HLint is a tool that provides suggestions to improve Haskell programs. C-c C-v helps to run hlint on a buffer. Make sure you have the tool installed on your system before using it. For example, running C-c C-v on the above literate quicksort Haskell program suggests:
-*- mode: compilation; default-directory: "~/" -*-
Compilation started at Thu Jun 6 21:31:54
hlint QuickSort.lhs
QuickSort.lhs:6:22: Warning: Redundant bracket
Found:
(quicksort lesser) ++ [p] ++ (quicksort greater)
Why not:
quicksort lesser ++ [p] ++ (quicksort greater)
QuickSort.lhs:6:44: Warning: Redundant bracket
Found:
[p] ++ (quicksort greater)
Why not:
[p] ++ quicksort greater
2 suggestions
Compilation exited abnormally with code 1 at Thu Jun 6 21:31:54