University of Glasgow

Haskell-Mode Users Manual

Version 1.0

December 1, 1995

Department of Computing Science University of Glasgow Glasgow, G12 8QQ

Scotland

1 Introduction

This manual describes Version 1.0 of The Glasgow University Haskell major mode for Emacs. The major mode automates the indentation of Haskell programs and as such is a valuable tool to any Haskell programmer.

1.1 A Word About Emacs and Haskell

Emacs is a large and, at times, complex text editor. This document assumes a familiarity with Emacs and its terminology. Any readers new to Emacs may wish to work through the introductory tutorial provided by the editor. Due to the vast range of facilities Emacs provides, the learning curve is initially steep but a little perseverance will bear fruit. The tutorial takes about ten minutes to complete and introduces the basic navigational features. To access this tutorial, enter Emacs by typing emacs on the command line. Once an Emacs window has opened up, press control and h. Next type t. Now just follow the instructions on the screen!

A knowledge of Haskell is imperative if the reader is to make any use of Haskell-mode. Haskell is a new, and by this very virtue, exciting functional programming language. Functional programming is very different to programming in an imperative language like Pascal or C. A purely functional language uses functions as its sole means of programming. Functional languages are declarative and so definitions of functions need not be declared before they are, say, used by another function. All these features are convenient for programmers but make the indentation of Haskell programs difficult to predict.

Haskell-mode deals solely with the indentation of Haskell programs. As far as possible Haskell-mode provides flexibility and tailorability in order to suit individual tastes but due to the huge range of possible preferences, only the most “popular” styles are catered for.

1.2 Haskell-Mode Version 1.0

This is the ‘alpha’ release of the Glasgow Haskell major mode for Emacs. I would appreciate any and all user comments, suggestions and bug reports. I am releasing the source of Haskellmode, so users are free to modify the major mode to better suit their individual preferences if need be. I would however, in the interest of keeping some kind of consistency, appreciate being kept informed of any useful changes made so that I can incorporate it into my version.

The source was originally written at Glasgow University by Richard McPhee, Andrew Preece, Charles Schofield and Scott Williams with Kevin Hammond. This is a patched, mostly working (!) release and I can guarantee nothing !

Users may send all related communication to the following address:

   • gem@minster.york.ac.uk

1.3 Installation

Add the following lines to your .emacs file (usually in your home directory),

(setq auto-mode-alist (cons '("\\.hs$" . haskell-mode)
                      (cons '("\\.hi$" . haskell-mode)
                      (cons '("\\.gs$" . haskell-mode)
                       auto-mode-alist))))
(autoload 'haskell-mode "haskell-mode" "Major mode for editing Haskell." t)
(setq load-path (cons "/myhomepath/myemacsdir" load-path))
   where of course /myhomepath/myemacsdir is the directory where the haskell-mode.elc

file is stored.

2 Using Haskell-Mode

Before using Haskell-mode, you must first configure your .emacs file as described in section 1.3. Once this file has been modified, the Emacs editor will enter Haskell-mode whenever a file with extension .hs or .hi is visited. Here, at the University of Glasgow, the extension .gs (gofer source) is also included. Haskell-mode may also be entered manually by M-x haskell-mode. Emacs will indicate that Haskell-mode has been entered by displaying (Haskell) in the black status bar at the foot of the Emacs window.

2.1 The Features of Haskell-Mode

The primary features of Haskell-Mode are as follows:

• Tabs are converted to spaces before delete is applied.

• Brackets “{ .. }”, “( .. )”, “[ .. ]” are matched automatically.

• Insertion of line comments “--” (M-;). A comment is inserted at an appropriate place on the current line.

• Goto line number (C-c g).

• Indent current line (tab).

• Reindent current line then newline and indent (linefeed or C-j).

• Futher indentation, specifically for use when continuing function applications over several lines (C-c f).

• Lesser indentation, for coming out of nested indentations (C-c b).

• Indentation of a region (C-c r). Indents the region between point and mark.

• A choice of standard indentation styles. These basically configure the Haskell-mode local variables, which are discussed next, to produce a recognised style of indentation.

2.2 Tailoring Indentation

To complement the indentation of Haskell code, there are several local variables in Haskellmode which you may set in order to better reflect your personal stylistic tastes. These are described next. The Emacs key bindings to alter the variables are given beside each name or, alternatively, the variables which end with -offset may be altered in the mini-buffer by M-x set-“variable name”. These variables may take any value from 0–10.

haskell-indent-offset (C-c n) This variable holds the value of the standard indentation for continuing Haskell code under the scope of an expression. The default is 4.

haskell-list-offset (C-c l) This variable holds the value of the extra indentation for continuing a list. The default is 4.

haskell-let-offset (C-c e) This variable holds the value of the extra indentation for a let expression. The default is 4.

haskell-case-offset (C-c c) This variable holds the value of the extra indentation for a case statement. The default is 4.

haskell-if-offset (C-c i) This variable holds the value of the standard indentation for an if statement. The default is 4.

haskell-then-offset (C-c t) This variable holds the value of the standard indentation for an then keyword beyond its corresponding if. The default is 0.

haskell-where-offset (C-c w) This variable holds the value of the extra indentation for the code following a where clause on a line. The default is 4.

haskell-where-threshold This variable holds the value of the column beyond which where clauses will no longer be positioned under or after its corresponding equality. In this situation, the where is indented as in haskell-std-indent-where (see below). The default is 35.

set-haskell-where-threshold This function allows you to change the column at which a where clause is no longer indented under or after its corresponding equality. The value may be 0–100.

haskell-comment-column This variable holds the value of the column which line comments “--” will be inserted to by use of M-; when there is code already on the line to be commented on. The default is 35.

set-haskell-comment-column (C-c ;) This function allows you to change the value of the local variable, haskell-comment-column. You input the value, which may be any number from 0–100, from the mini-buffer.

haskell-nest-ifs Setting this variable to t means that if statements will be nested. The value of this variable is toggled between t an nil by M-x haskell-nest-ifs. For example,

  1. haskell-nest-ifs t
    foobar a = if a == 1 then a * 10
               else if a == 2 then a * 20
                    else if a == 3 then a * 30
                         else a * 40
  2. haskell-nest-ifs nil
    foobar a = if a == 1 then a * 10
               else if a == 2 then a * 20
               else if a == 3 then a * 30
               else a * 40

haskell-align-else-with-then Setting this variable to t means that else keywords in an if statement will be lined up with the corresponding then keyword. The value of this

variable is toggled between t and nil by M-x haskell-align-else-with-then. For

example,

  1. haskell-align-else-with-then t
    foobar a = if a == 1 then a * 10
                         else a * 20
  2. haskell-align-else-with-then nil
    foobar a = if a == 1 then a * 10
               else a * 20

haskell-align-where-with-eq Setting this variable means that any where clause will be positioned under its corresponding equals sign. The command sequence to set this variable is M-x haskell-align-where-with-eq This produces the following style:

foobar a = b
         where b = a * 10

haskell-align-where-after-eq Setting this variable means that any where clause will be positioned under the first non-space character after its corresponding equals sign. This variable is set by the command M-x haskell-align-where-after-eq. The indentation style is as follows:

foobar a = b
           where b = a * 10

haskell-std-indent-where Setting this variable means that any where clause will be positioned the standard offset (haskell-indent-offset) past any previous layout column. This variable is set by the command M-x haskell-std-indent-where. An example containing nested where clauses is necessary in order to illustrate the indentation after the layout column. In this example, haskell-indent-offset is set to

     foobar a = b
        where b = d
                 where d = a * 10

haskell-always-fixup-comment-space Setting this variable means that inserting or realigning a comment always causes one space to be positioned after a line comment “--”, whether or not one space originally occurs. To toggle between the two values of this variable, which are t and nil, type M-x haskell-always-fixup-comment-space. The new value of the variable will be displayed in the mini-buffer.

haskell-std-list-indent Setting this variable means that a function application which spans more than one line will have its parameters indented haskell-list-offset spaces past the beginning of the function name. If it is not set, then the parameters will be lined up underneath any existing parameters. For example,

       1. haskell-std-list-indent t
         foobar a b c = (functionName a
                            b
                            c)
       2. haskell-std-list-indent nil
         foobar a b c = (functionName a
                                      b
                                      c)

2.3 The Limitations of Haskell-Mode

Before you use Haskell-mode, it is probably a good idea for you to scan this section to discover how the major mode works and the limitations Haskell imposes on it.

Haskell programs avoid the use of semicolons, or some other kind of line terminator, to mark the end of equations, declarations and so forth, by relying on layout. Essentially, this requires that declarations are “lined up in columns”. The task of ensuring that layout is adhered to is left up to the programmer. Naturally, the programmer has no difficulty in determining when and where to begin a new declaration. Unfortunately, the automation of such decision-making is somewhat more complex. Indeed, short of parsing the whole program, which even then may not solve the problem entirely, this feat of prognostication is generally impossible! Most other languages, certainly those which have an Emacs major mode, do not have to “guess” as much as in Haskell.

This leads to the fact that Haskell-mode sometimes incorrectly guesses the indentation. How Haskell-mode copes with these situations is discussed next.

2.4 Performing Indentation

There is not too much to remember when using haskell-mode’s indentation features. Rather simply, there are three basic keys:

   • return

This key takes a newline and performs no indentation.

   • tab

This key indents the current line according to context.

   • linefeed

This key reindents the current line and then indents the next line according to context.

Notice how the linefeed key reindents the current line. There is quite a subtle reason for this. Consider the following contrived example to illustrate this point.

foo a = b + a     {linefeed}
      where b = d

After typing where b = d in Emacs, the linefeed key is pressed. It is valid Haskell syntax to introduce the value of the variable d under another where clause. So say this is your intention. However, haskell-mode does not know that you have finished the declarations under this clause, so the linefeed key actually positions the cursor (indicated by !) under the b.

foo a = b + a
      where b = d {linefeed}
            !

However, haskell-mode knows that, should you type where, the line must be repositioned accordingly. Hence, pressing linefeed at the point illustrated as follows

foo a = b + a
      where b = d
            where d = 1 {linefeed}

reindents the current line to its correct location, as dictated by the where, producing the following:

foo a = b + a
      where b = d
              where d = 1
                    !

Furthermore, notice that haskell-mode does not appreciate that this is the end of the function declaration, so the cursor is positioned underneath the d. To remedy this, you must press return. Having done so, the cursor will move down a line and back to the left hand margin. The reason for pressing return rather than linefeed is that blank lines are ignored within a function declaration. This enables you to intersperse your code with blank lines, which helps make code more readable, but unfortunately means that Haskell-mode cannot predicate the end of a function. Note that, for exactly the same reason, after typing the next line, you should press return to take a newline and then use tab to perform indentation.

In general, the current line is reindented before taking a newline since haskell-mode may have originally only guessed this line’s indentation.

2.5 Overriding Indentation

Humans are very hard to please. When it comes to programming, everyone has a different opinion and style so the occasional situation will arise where Haskell-mode does not indent to the position that you wanted. The easy way to overcome this is by applying either the space or the delete key to manually adjust the indentation. However, remember that Haskell-mode reindents the current line when linefeed is pressed, so if the indentation has been manually altered, it is important that the return key is used to take a newline. You may then indent this new line by applying tab.

Extending a function application, which is not enclosed in parenthesis, highlights the difficulty of predicting indentation. Consider the following example:

foo a = bar a   {linefeed}
      !

As indicated, the cursor moves under the equality, since Haskell-mode reasonably expects a where clause. However, it could be the case that the function bar takes more than one parameter. Should this be the case, then the cursor ought to be positioned under the a or, if preferred, indented by a set amount after the b of bar.

Haskell-mode relies on you to indicate that extra indentation is required in this situation. To perform this indentation, type C-c f. Exactly what happens next depends on the value of the local variable haskell-std-list-indent. If this is set (ie. non-nil), then the cursor is indented by haskell-list-offset beyond the first non-space character after the equality. Otherwise, the line is indented to under the first non-space character after the (assumed) function name.

Conversely, you may wish to come out of a level of indentation. This situation could arise when a statement has local declarations:

foobar a = let a = d
         where

                     d = b
     b = 2  {linefeed}
     !

Here, you may wish to declare another variable under the scope of the let expression. Unfortunately, Haskell-mode will not spot the end of the where clause and so will keep positioning the cursor as indicated. To remedy this, the user may type the command sequence C-c b. This will successively reduce the indentation to the last level which is less than the current one. So, continuing this example:

foobar a = let a = d
         where
                     d = b
                     b = 2
       !            {C-c b}

After this command, the cursor is positioned as shown. Again remember that since you have altered the indentation manually, you should use return to take a newline rather than linefeed since the line would be reindented to the original position.

3 Known Shortcomings of Haskell-Mode

There are a number of areas in which Haskell-mode is deficient at the time being. Part of this is due to the fact that Haskell programs can be quite unpredictable and partly because there has just not been time to include all possibilities. It is hoped that most of these limitations will be rectified but include a list of all “problem” areas for completeness.

3.1 What is Wrong and What Can Be Done About It?

Here is a summary of all the things that we know Haskell-mode cannot cope with. Some of the points may already have been mentioned in previous sections but here we collect the major points to provide a more concise picture of Haskell-mode and its limitations. The main way to remedy any indentation problems is to use the space or delete keys to reposition the line manually as required.

1. Regional comments, “{-...-}”. Haskell has two ways of commenting code. Due to time constraints, Haskell-mode only recognises “--” as comments in the sense of ignoring all text included. An inclusion of both comments would have resulted in rewriting a lot existing Emacs Lisp functions.

2. Ambiguity in moving out indentation levels. When performing this action, if a previous line has two possible indentations then Haskell-mode will only “notice” the last indentation. For example,

foobar a = let b = case a of 1 -> 10
                             2 -> 12  {linefeed}
                             !

The cursor is left at the illustrated position. If you require to extend the let to include more variables, then the use of C-c b will unfortunately be of little use. In this situation, the cursor would be positioned at the left hand margin. One way to overcome this problem, which you may or may not find satisfactory is to “break up” the let like so:

foobar a = let
               b = case a of 1 -> 10
                             2 -> 12  {linefeed}
                             !

in which case, the use of C-c b would position the cursor underneath the b as required. If this situation is too large a change in style, then the only other way to reconcile the situation is to use the delete key.