University of Glasgow

A Haskell Major Mode for Emacs

December 1, 1995

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

Scotland

Contents

1 Introduction 1

2 A Background to Emacs and Haskell 3 2.1 What is Emacs? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 2.2 How Emacs Works . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2.3 A Taste of Programming in Emacs Lisp . . . . . . . . . . . . . . . . . . . . 5 2.4 The Haskell Programming Language . . . . . . . . . . . . . . . . . . . . . . 7 2.5 A Haskell Major Mode for Emacs . . . . . . . . . . . . . . . . . . . . . . . . 8 2.6 Different Indentation Styles . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

3 What Haskell-Mode Does 11 3.1 Some Simple Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 3.2 Dealing with Complicated Indentation . . . . . . . . . . . . . . . . . . . . . 15

4 Conclusion 18 4.1 Major Obstacles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 4.2 Benefits Of Participation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 4.3 Assessment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

5 The Summary Log 20

6 Status Report 22 6.1 Limitations and Known Bugs . . . . . . . . . . . . . . . . . . . . . . . . . . 22 6.2 Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

A Haskell-Mode User Manual 25

B The Emacs Lisp Code for Haskell-Mode 26

1 Introduction

Haskell is a new, and by this virtue exciting, functional programming language. It is fast becoming the standard functional language in academic institutions and its application is now being seen within industry. Functional languages, unlike the conventional procedural languages such as Pascal or C, rely solely on functions and the values they return in order to achieve their goals; There is no global store (global variables) in a purely functional language. Function definitions need not be declared before they are used as is usually the case in procedural languages. This affords the programmer a greater degree of flexibility with the layout and format of Haskell programs.

Any aesthetic computer scientist will talk at length about the virtues of a well laid out program. Such a program is easy to read, follow, and understand. As a result, the time it takes for another programmer to comprehend the program is reduced and modifications to the software can be made much more easily.

In addition to the benefits of a well formatted program, the Haskell language itself imposes quite rigid restrictions on its programs; They must adhere to layout. This basically requires that declarations are “lined up in columns”. As more and more programs are written in Haskell, more and more programmers discover just how tedious and frustrating a task it is to format a Haskell program with any degree of consistency or clarity. Text editing programs are generally used to automate the process of good code layout but since Haskell is a relatively new programming language, text editors provide little support for the development of well laid out Haskell code.

Emacs is widely used text editor which also incorporates an integral interpreter for a programming language called Emacs Lisp (or elisp for brevity). The interpreter provides the facility to extend the text editor so it can recognise the specific layout requirements of a particular programming language. Such an extension to Emacs is called a major mode. This project resolves the problem of formatting Haskell programs by creating a Haskell major mode for Emacs.

This, however, is a difficult task. Even with Haskell’s rules for layout, there is still such a large range of different indentation styles which people like to use that catering for all of them is a task which proved too time consuming. Haskell-mode supports the most popular styles and, as much as possible, allows the user to tailor certain aspects of indentation to better suit her needs.

This report assumes that the reader has some exposure to Haskell and Emacs. A knowledge of Emacs will allow the reader to understand the execution and terminology of the various features provided by the major mode. In addition, a familiarity with the Haskell programming language is required in order to fully appreciate the indentation and layout functions afforded by the mode. Readers interested in the actual coding of the major mode are encouraged to work through the elisp tutorial [3]. To read this file, firstly enter Emacs by typing emacs on the command line. Once an Emacs window opens up, press escape then type x. After this, type info.

Here are the primary features of the Haskell major mode for Emacs.

Haskell mode is entered automatically when a file with extension .hs, .hi or .gs is visited. These extensions stand for Haskell source, Haskell interface and Gofer ^{1} source respectively. The Emacs status line, which is the black line at the bottom of the Emacs window, displays the mode name as (Haskell). The primary feature of Haskell-mode is the provision of automatic indentation of Haskell code, conforming to the current context of the program. This is performed by pressing the tab or line-feed key when in Haskellmode. The major mode provides several predefined indentation styles which follow the more “popular” styles. In addition, the user may customise certain aspects of indentation to allow for personal stylistic taste. The major mode automatically inserts line comments, “--”, and performs parentheses matching which ignores parentheses in comments or strings.

One large, external topic to the project was what actually makes good indentation. Why is one indentation better, or clearer, than another? Questions like these are research questions in themselves and are beyond the scope of this project. What this report deals with is the accommodation of different styles rather than the justification as to why these are good or bad styles.

Haskell-mode is a working and useful software tool for any Haskell programmer. However, the very nature of Haskell imposes some limitations on the software. These limitations are discussed in section 6.

Time normally plays a limiting factor in any software development project and this one was no exception. It was hoped to have included compilation features in the major mode which would have created an interactive environment within Emacs to compile and run Haskell programs. However, due to time constraints, this was not achieved.

Another useful feature would have been to extend Haskell-mode to recognise and indent literate scripts. These are text files which contain lines, beginning with the symbol “>”, which should be interpreted as Haskell code and the rest of the text ignored. Once again, time was the limiting factor for the inclusion of this facility.

The next section of the report provides a background into Emacs and Haskell which explains more about major modes, indentation styles for Haskell and the difficulties in making a Haskell major mode.

Section 3 deals with what Haskell-mode actually does for a programmer and the ways to perform complicated indentation.

In Section 4, there is a discussion on the difficulties which the group had to overcome. It highlights what the team felt were the most academic and personal benefits of participating in the project along with a general assessment.

Following this, there is a summary of the workings of the team and the development of the design, implementation and testing of the project.

In Section 6, there is a discussion regarding the final status of the software. This includes all the limitations and known bugs in the system as well as a detail of the testing of Haskell-mode.

1Gofer is a widely used Haskell interpreter, written by Mark P. Jones.

Appendix A contains the Haskell-mode User Manual which is a comprehensive overview into the software and how best to use it.

Finally, Appendix B contains the full sources of Haskell-mode written in the Emacs Lisp programming language and afterwards is a list of references.

2 A Background to Emacs and Haskell

2.1 What is Emacs?

Emacs is a widely used text editor. It is a “display” editor in the sense that the text being edited is normally visible on the screen and is updated automatically as commands are typed in. Far and away, the most common use of Emacs is to create or alter text files and computer programs. Emacs loads in a file to a “buffer” which can then be manipulated until it is finally saved again to disc.

If you are unfamiliar with Emacs, you should work through the introductory tutorial provided by the editor. To access this, firstly enter Emacs by typing emacs on the command line. Once an Emacs window has opened up, press the control key and the h key together. After this, press the i key. Now you will have entered the tutorial. It takes about fifteen minutes to work through and presents a good range of the navigational features of Emacs.

If you have used any other text editors, you will probably notice several differences between Emacs and your editor of choice. Perhaps the most obvious difference, certainly if you are used to a personal computing environment, is that the mouse plays an insignificant role with regard to cursor motion and manipulation of text. All Emacs commands are driven by the escape and control keys. In Emacs jargon, these keys are referred to as M (or Meta) and C, respectively. But a command key controlled environment? How user unfriendly! Well, perhaps initially, but once you become fluent with the commands, the speed of executing them is so much quicker than lifting your hand off the keyboard, reaching for the mouse, pressing its buttons and so forth.

So Emacs is a large, and at times, complex editor. However, it provides such a huge range of features that, once you become more confident with using its commands, no other editor will ever measure up! Within a buffer, Emacs allows you to manipulate individual characters, words, sentences, paragraphs, and even the whole buffer itself. However, the insertion and deletion of text is only the beginning of what Emacs provides for you.

Without listing all of Emacs’ attributes, of which there are many, we instead focus on those ones which are exploited in this project. Recall that one major use of Emacs is to create and alter computer programs. Any reader who has ever programmed before should be well aware of how important it is for programs to be well laid out. Such a program is easier to read and, thus, easier to comprehend. Indeed, many programming languages, over time, develop a standard style which programmers tend to adopt (to a degree at least). That is, a standard indentation style is adopted. For example, when writing a program we may wish to accentuate the fact that a group of expressions are related to another expression by uniformly indenting them underneath the “owning” expression.

Emacs indents code automatically for some programming languages. When this facility is provided, the language is said to have a “major mode” for Emacs. This is undoubtedly one of the most helpful tools which Emacs provides. For a language which has a major mode, the time and effort it takes a programmer to adhere to the standards of indentation are significantly reduced. Generally, the line-feed and tab keys perform automatic indentation; The line of program code is indented with respect to the context that it appears in. Major modes also provide other, more specialised, facilities depending on the nature of the programming language it is written for. It is common to see automatic comment insertion as well as parentheses matching, whereby the cursor is momentarily moved onto the corresponding open parenthesis when a close bracket is typed. In general, a major mode tailors Emacs in order to provide specialised commands and abilities for a specific programming language. However, only the most popular languages have a major mode for Emacs. This may appear to be a considerable drawback but, in fact, Emacs solves this problem very effectively.

Emacs is an extensible editor. This means that you can go beyond the simple customisation of existing commands; You can write entirely new ones. In order to discover how this is done, we need to look a little deeper into the workings of Emacs.

2.2 How Emacs Works

Most of Emacs’ functions for moving about and editing the text in a file are written in the programming language, Emacs Lisp ^{2}. Emacs has a built-in interpreter for this language and as a result, making extensions to the text editor is a matter of writing new functions in Emacs Lisp. However, Emacs Lisp is more than a mere “extension language”; It is a full computer programming language in its own right. You can use it as you would any other programming language.

Whenever you give an editing command to Emacs Lisp, such as the command to move the cursor or to scroll the screen, you are evaluating an expression, which contains a function. So, when you type keys, you cause the Lisp interpreter to evaluate an expression and that is how you achieve your results. Even typing plain text involves evaluating an Emacs Lisp function, in this case, one that uses the “self-insert-command” function, which simply inserts the character you typed. The functions you call by typing keystrokes this way are called “interactive” functions, or “commands”.

There are numerous books which deal with Lisp which the reader may refer to in order to become more familiar with it. Alternatively, you could learn everything you need to know about Emacs Lisp from the ever informative Emacs info file. To read these pages, type the command sequence M-x info in Emacs. Remember that this means press escape, then x and then type info. There are two references you can look at. The first is the elisp-tutorial which is a comprehensive introduction into using elisp and tailoring Emacs to suit your needs. The second document is the elisp reference manual [3] which basically contains a description of all the functions which Emacs Lisp provides. The language itself is not very big but what does make Emacs Lisp enormous is all the predefined “goodies” it provides: the functions for manipulating text in a buffer. There are seemingly endless amounts of functions and every venture into the elisp reference manual uncovers more useful functions. For the interested reader, browse the next section to see what programming in Emacs Lisp is like.

2Emacs Lisp, or elisp for brevity, is a dialect of the Lisp programming language.

2.3 A Taste of Programming in Emacs Lisp

In this section, a little knowledge of Lisp is assumed. We now present a brief glimpse into how to program in Emacs Lisp and thus allow you to extend your own Emacs environment. The uninterested reader may skip this section without a loss of background with regard to the rest of the report.

Emacs Lisp is a functional programming language of sorts. We emphasise the “of sorts” since functional language purists tend to dismiss Lisp since it is not a purely functional language. If any of this terminology is unfamiliar to you, don’t worry. All of it will be described in more detail in the next section when we talk about Haskell.

As we mentioned in the last subsection, you manipulate files in Emacs by calling functions. Many of these are primitives which you can use when making your own functions. The most common functions, which are used regularly in Emacs Lisp programs, include “forward-char”, “forward-word”, “search-forward”, “search-backward” and so forth. Any reader competent with Emacs will have used these functions, and many others, quite often. The most subtle point to consider here is that a whole host of Emacs Lisp functions move the point. The point is Emacs jargon for the position of the cursor in the buffer. The subtlety of this is highlighted with the following example. If, in an Emacs program, you were required to search back for some keyword, then you may use the “search-backward” function. However, this has the effect of moving the point to the location of the word you were searching for. The last thing the user of a major mode would want is to find the point moved to some other location in the buffer when she issues some command which, ostensibly, should not really move the point. This leads us to the most useful and regularly used Emacs Lisp function: “save-excursion”.

The save-excursion function saves the position of the point and the mark (a simple marker so that you can deal with “regions”). When this function terminates, the original values of the point and mark are restored. Writing Emacs programs requires so much “hidden” manipulation of the buffer that save-excursion will soon become your most commonly used function.

In the case of the major mode for Haskell, a large number of searches require comparisons of words to the text in the buffer. This leads us onto describing the “looking-at” function. This function examines the text in the buffer, from the point onwards, and compares it to its argument. For example,

(looking-at ``where'')

would return t (the Emacs Lisp truth value for true) if, and only if, the point was positioned on the w of the where.

We have now introduced a few of the most common functions, so let us jump straight into a small, and somewhat contrived, example to illustrate some of the techniques of programming in Emacs Lisp. Consider the following function which returns a truth value as to whether there is the word where on the current line in the buffer.

(defun where-on-linep ()
   ``Returns a truth value as to whether there is a `where' on
current line.''
   (interactive)
   (save-excursion
      (beginning-of-line)
      (re-search-forward
       ``\\bwhere\\b''
       (save-excursion
          (end-of-line)
          (point)))))

There are few places in this function definition where Emacs Lisp differs from standard Lisp. Now we step through the definition in more detail to discuss the finer points of elisp.

The defun statement will be familiar to readers with a knowledge of Lisp; This just defines a function called “where-on-linep”. Notice that the function name ends, somewhat oddly, in a p. This is an elisp convention to indicate that the function is a predicate, ie. it returns a truth value. The next line is a description of the function which will be shown if the user should type C-h f where-on-linep whilst in Emacs. The next line is the function, “interactive”. The use of this function, which is optional, tells Emacs that you want to make this function a “command” ie. you want to be able to call this function from the mini-buffer. The mini-buffer is the line at the bottom of the main buffer where you type commands after pressing M-x. So in our situation, you could type M-x where-on-linep and you would call the function.

Now we come to the body of the function. Immediately, you see the application of the save-excursion function. This is due to the fact that the next line of code moves the cursor to the beginning of the current line. The function “re-search-forward” is similar to searchforward except the former takes a regular expression as its first argument. Rather than go into regular expressions (or, in Emacs jargon, “regexps”) here, the interested reader will find an informative discussion of them in the elisp reference manual [3], under Searching and Matching. The second argument to re-search-forward is the limit of the search, in this case the end of the line. So we actually move to the end of the line and take the point. The second save-excursion is used to return the the point to the beginning of the line after we have established the end of the line limit.

This section was just a small taste into the sort of tasks that Emacs Lisp performs when manipulating a buffer. The key point to note is the use of save-excursion since the user would become very disconcerted if the cursor was to jump unexpectedly about the buffer. We now move onto to introducing the Haskell programming language and Haskell-mode, the Haskell major mode for Emacs.

2.4 The Haskell Programming Language

In September 1987 a meeting was held at the conference on Functional Programming Languages and Computer Architecture in Portland, Oregon, to discuss an unfortunate situation in the functional programming community: there had come into being more than a dozen non-strict, purely functional programming languages, all similar in expressive power and semantic underpinnings. There was a strong consensus at the meeting that more widespread use of this class of functional languages was being hampered by the lack of a common language. It was decided that a committee should be formed to design such a language, providing faster communication of new ideas, a stable foundation for real applications development, and a vehicle through which others would be encouraged to use functional languages.

And so Haskell was born! This quote, taken from [1], is a rather official history of the Haskell programming language. The key point to note here, however, is that Haskell is a functional programming language. A functional language is distinct from a procedural language, such as C or Pascal. Unfortunately, a description of these terms involves a certain degree of fluency with programming languages. In purely functional languages, of which Haskell is an example, all computations are performed by evaluating expressions (syntactic terms) which yield values (abstract entities that are regarded as answers). Haskell has no commands that operate by implicit side effects to a global store.

Haskell differs from languages such as Lisp or Basic because of its complex use of types. Haskell is a typeful language and its type system is somewhat richer than most other languages. Initially, Haskell can be frustrating to a newcomer until she becomes fully aware of the full power and complexity of Haskell’s type system. In Haskell, every value has an associated type. Examples of expressions include atomic values such as the integer 3, the character ’a’, and the addition function +, as well as structured values such as the list [1,

2, 3] and the pair ('a', 4).

In Haskell, every value is “first-class” and can be passed as an argument to another function. The types in Haskell cannot be used in this way and are better thought of as a description of a value. The association of a value with its type is called its typing. Using the examples of values and types above, we write typings as follows:

      'c' :: Char
        3 :: Int
['h','b'] :: [Char]
  (4,'d') :: (Int,Char)
        + :: Int -> Int -> Int

The symbol :: can be thought of as meaning “has type”. The first two examples are straight forward. The third typing means a list whose elements are of type Char. The fourth is a pair whose elements have the associated types and the last is a function which takes two parameters of type Int and returns a result of type Int. Haskell is a statically typed language which means that all type errors are reported at compile time. Hence, Haskell does not allow expressions of the form 'c' + 'f'.

The type [a] is called a polymorphic type and represents the type of lists of anything,

for example, [Char] or [(Int,Int)].

The fundamental building block of a Haskell program is the function. These are usually fairly self-explanatory. Since Haskell is a functional language, there is no need for the programmer to declare explicitly the beginning and end of a function. This allows Haskell functions to take many different shapes as opposed to older languages, such as Pascal, where a function has to have a header and is clearly defined within the keywords begin and end. An example of a simple Haskell function, which returns the product of its three inputs, is:

multiply3 :: Int -> Int -> Int -> Int
multiply3 first second third = answer
             where answer = first * second * third

The first line is the type declaration and can be omitted.

2.5 A Haskell Major Mode for Emacs

Haskell programs avoid the use of semicolons, or some other kind of line terminator, to mark the end of equations, declarations, etc. by using a two-dimensional syntax called layout. Layout essentially relies on declarations being “lined up in columns”. The rules of thumb for layout are:

1. The next character following any of the keywords where, let, or of is what determines the starting column for the declarations in the expression being written. Thus we can define the declarations on the same line as the keyword, the next line, or wherever.

2. Be sure that the starting column is further to the right than the starting columns associated with the immediately surrounding clause (otherwise it would be ambiguous). The “termination” of a declaration happens when something appears at or to the left of the starting column associated with that binding form.

The use of layout is encouraged since it does away with explicit declaration lists and so increases program readability. However, the indentation of Haskell programs has become one of the most tedious tasks for a programmer to perform.

On faith, it is absolutely horrible to to manually indent Haskell code. It is tedious to line up declarations under each other as you conform to the layout rules. Consider having to perform the following operation:

foobar a = let b = d * 5
                 where d = a + 4
               c = 3
           in
               b ^c

Once the programmer has finished the where clause, she has to carefully move the cursor to just under the b in order to follow the rules of layout. If there had been more declarations in the where clause, so that the line c = 3 was further down the screen, it would have been even more difficult to indent the line properly.

To overcome the insertion and deletion of spaces to align declarations in layout, a commonly adopted method is to align all declarations in “tab” columns. However, the constant insertion of tabs used by most programmers gives vastly inconsistent indentation styles and leaves program readability for dead. Having said this, though, a lot of programmers have adopted this format to a lesser extent and line up certain keywords such as ::, =, and ->. This can be quite readable at times, but the whole reason why this style was adopted in the first place was because Haskell programmers became accustomed to making liberal use of the tab key!

For all these reasons, a Haskell major mode, which would automate all these tasks, would be a valuable tool to any Haskell programmer.

2.6 Different Indentation Styles

Given Haskell’s layout rules, there are a number of different indentation styles which would adhere to their requirements. We deal with very simple examples throughout only to illustrate the different possibilities.

1. Consider some of the styles for the indentation of a where clause.

(a) Align under the corresponding equality.

         foo a = b
               where b = a * 10

(b) Align after the corresponding equality.

         foo a = b
                 where b = a * 10

(c) Align where a standard amount under the line containing the equality.

         foo a = b
           where b = a * 10

(d) where at the end of the line.

         foo a = b where
           b = a * 10

(e) The where on a line on its own. Any of the first three styles for positioning the where keyword may apply.

         foo a = b
               where
                 a = a * 10

2. There are a number of ways that a Haskell programmer may wish to indent a case statement

(a) The expressions after the of keyword.

foo a = case a of 1 -> 2
                  2 -> 7

(b) The expressions on the next line.

foo a = case a of
          1 -> 2
          2 -> 7

3. Styles for an if statement:

(a) Align else keywords with the corresponding if.

foo a = if a == 1 then 4
        else 7

(b) Align else keywords with the corresponding then.

foo a = if a == 1 then 4
                  else 7

(c) Beginning each expression on the next line of an if statement.

 a = if a == 1 then
          4
        else
          7

(d) Beginning each expression on the next line of an if statement when we want to line up the then and else.

foo a = if a == 1 then
                    4
                  else
                    7

4. Lists and functions can be indented in several ways.

(a) Indent a standard amount after the function name.

foobar a b = (foo (a * 3)
               (b * 5))

(b) Indent to the first argument (if there is one) after the function name.

foobar a b = (foo (a * 3)
                  (b * 5))

These cases above show the differences in style for the most important Haskell expressions. In summary, automatic indentation is particularly useful; The speed and effort involved in typing in a program can be greatly reduced and also a strict convention of indentation is adhered to, so making the program completely uniform.

Haskell-mode has to be aware of the most common indentation styles and cater competently with all the possibilities. In the next section, we look at what Haskell-mode does and doesn’t do.

3 What Haskell-Mode Does

In this section, we demonstrate what Haskell-mode does by the way of examples. We begin with some simple examples and then move onto more complicated examples. We refer the reader to the user manual, appendix A, which complements and introduces this section.

3.1 Some Simple Examples

The major mode formats the line of Haskell code by inserting tabs and spaces in order to move the cursor and code to the correct indentation. The purpose of this is to save the user having to do this manually. Indentation is important as it indicates the context of the line of code in question. The major mode also makes the indentation neater.

The user may type in code as follows:

data Colour = Red
            | Blue

In Haskell-mode, when the line-feed key is pressed after c, the cursor is positioned under the = sign, ready to start the next line with |. Successive presses of line-feed continue to position the cursor in this column. To move the cursor to the left hand margin, the user must press return (or C-c b which we will encounter later).

The major mode also copes with guarded expressions such as the following:

foobar a
    | a==1  = 2*a
    | a==2  = 5*a

or, for an alternative style:

foobar a | a==1  = 2*a
         | a==2  = 5*a

Haskell-mode allows the user to have blank lines within a function. This means that Haskell-mode has difficulty in telling exactly when a function declaration has ended. Normally, the major mode relies on the user to type a return which will place the cursor at the left hand edge of the screen. However, Haskell-mode does notice if two successive function declarations have the same name. In this case, the line will not be erroneously indented as it might have been otherwise. To illustrate this point, consider the following example:

foo a = b
      where
         b = a * 10

Haskell-mode does not parse the variables in a function to see if they have all been declared. Thus, it does not know that any more equations are not scoped under the where clause. That is, the next function declaration will be assumed to be scoped under the where clause. To avoid this problem, the user should press return after the 10 and begin any new expressions on the left hand margin. It is important that the user presses return after this line also since Haskell-mode reindents the current line when line-feed is pressed. For more information on this, the reader is referred to the user manual.

Comments can be inserted on a line by typing M-; The comment will either be inserted at the start of a line if the cursor is positioned at the left hand edge or at the comment-column if there is code on the line. For example:

-- comment on its own line
foo a b = a * b           -- comment at comment-column
bar a b c d e = (a, b, c, d, e) -- comment on long code line

Haskell-mode indents let expressions in either of the following ways:

  1. let
         a = b
         c = d
     in
         b * d
  2. let a = b
         c = d
     in b * d

For where clauses, the actual clauses can either be typed starting on the same line as the where keyword or on the next line where the cursor will be positioned under the end of the where. Pressing line-feed will position the cursor under the previous clause ready to do another but if there are no more clauses pressing C-c b after line-feed will bring the cursor back to the previous indentation level. line-feed must not be pressed after this line as this will reindent the line to the level of the where clauses. Instead, Return then Tab should be used.

let a = c
      where
          c = d
    e = (f,g)
      where f = u
            g = v

With nested where clauses it is similar, C-c b being used to bring the cursor back to the previous indentation level. Each use of this command reduces the indentation level by one.

foo s t = j k
        where
            j = bar s m
              where
                  m = s * t
            k = t - 3           {C-c b}
foobar a = a * 10               {C-c b}

For type declarations, if they are split over several lines, they will be indented so they line up under the ::.

foo :: a -> b
    -> c

The emacs bracket matching facility ignores brackets that are inside comments as these are not part of the program and therefore should not match.

foo a = (b,                  -- comment ( bracket not matched
         c)

With if statements, there are a number of different styles that Haskell-mode caters for.

1. Align the else under the then.

     foo a = if a == 5 then 10
                       else 12

2. Align the else under the if.

     foo a = if a == 5 then 10
             else 12

3. The then does not appear on the same line as the if. If line-feed is pressed after the guard, the new line will be indented under the if by a set amount. This amount is normally 0.

     foo a = if a == 5
             then 10
             else 12

For case statements, the major mode arranges for the cursor to be positioned under the previous case so the choices are arranged in a column. To escape from this, use C-c b.

foo a = case a of 1 -> 100
                  2 -> 101
                  3 -> 102
bar a = a * 10       {C-c b}

When it necessary to split a function definition over two lines line-feed (or Return Tab if necessary) followed by C-c f will further indent to allow more parameters to be given.

foo a b c d = goo (hoo d) (hoo c)
                  (hoo b) (hoo a)

Again, Return Tab will have to be used at the end of the further indented line to avoid moving it back to its original indentation.

When items within brackets are listed in a column, the major mode arranges automatically for the cursor to be positioned under the previous item after each line-feed so the column is neatly arranged. When the brackets are closed the indentation level automatically reverts to the previous level.

roo a b c = (a,
             b,
             c)
soo g = h

The same happens for square brackets.

too d e f = [d,
             e,
             f]

3.2 Dealing with Complicated Indentation

Here are some examples that show what Haskell-mode can do with more complicated examples, which involve several keywords and their respective indentation patterns. These examples are taken from the Standard Prelude functions for Haskell, listed in [2].

showint :: (Integral a) => a -> ShowS
showInt n = if n < 0 then showChar '-' . showInt' (-n) else showInt' n
          where showInt' n r = let (n',d) = divRem n 10
                                   r' = chr (ord '0' + fromIntegral d) : r
                               in if n' == 0 then r' else showInt n' r'

The above code was produced by just pressing LineFeed after every line, apart from the first line as always which requires return to be pressed to start a new function.

(\\) :: (Eq a) -> [a] -> [a] -> [a]
(\\) = foldl del
     where [] `del` _y     = []
           (x:xs) 'del' y
     | x == y = xs
     | otherwise = x : xs 'del' y

The list difference function indents in the above way; it is set out in a different way in the report, as below. To achieve the below format (if that is your prefered style) then whitespace has to be added on the first two lines. Haskell-mode binds the tab key to one of its functions so the insertion of a (real) tab can be achieved by the command sequence C-q then press the tab key.

(\\)                              :: (Eq a) -> [a] -> [a] -> [a]
(\\)                              = foldl del
                                  where [] 'del' y          = []
                                           | x == y         = xs
                                           | otherwise      = x : xs 'del' y

floatProperFraction x = let (m,n) = decodeFloat x
                            b = floatRadix x
                        in if n >= 0
                           then (fromInteger m * fromInteger b ^n, 0)
                           else let d      = b ^(-n)
                                    (m',k) = divRem m d
                                in (fromInteger m',
                                    fromInteger k / fromInteger d)

The above function was produced in the normal way. In the report the style used indents after the if and the then and else are aligned, this can be achieved if report mode is used (with M-x report-mode) or be adding some whitespace after the cursor after the line in if

n >= 0.
round :: (RealFrac a, Integral b) => a -> b
round x = let (n,r) = properFraction x
              m     = if r < 0 then n -1 else n + 1
          in case signum (abs r - 0.5) of
                 -1 -> n
                 0  -> if even n then n else m
                 1  -> m

The above function was produced in normal Haskell mode it differs from in style from that shown in the report only by whitespace between round and :: and round x and =. it also differs by having a smaller indent on the last three lines. Using report mode indents the last three line by one whitespace more only (report mode is not able to add whitespace beteen the function name and the :: in a type declaration).

words :: String -> [String]
words s = case dropWhile isSpace s of
              ``'' -> []
              s'   -> w : words s ''
                      where (w, s'') = break isSpace s'

This function needs no manipulation when written in normal mode. In report mode it indents closely to the format shown in the report, except again space is not made between function name and the ::.

approximants p q = zipWith (:X) ps qs
                 where
                     ps         = gen unit (unit*a)
                     qs         = gen 0 1
                     unit       = signum p * signum q
                     (a:as)     = partialQuotients (abs p) (abs q)
                     gen x x'   = xs
                                where
                                    xs = x' : zipWith3 next as (x:xs) xs
                                    next a x x' = x' * a + x

This is the resulting layout of this function in normal mode. It differs from the layout in the report only on the aligning of the wheres. Using report-mode, the desired result is reached (the first where under the z of zipWith and all other declarations in the same column and the second where aligned after its corresponding equality).

approximants p q = zipWith (:X) ps qs
               where
               ps         = gen unit (unit*a)
               qs         = gen 0 1
               unit       = signum p * signum q
               (a:as)     = partialQuotients (abs p) (abs q)
               gen x x'   = xs
                            where
                            xs = x' : zipWith3 next as (x:xs) xs
                            next a x x' = x' * a + x

Here is an example where normal and report mode results do not differ.

instance Text Float where
readsPrec p = readSigned readFloat
showsPrec   = showSigned showFloat

This next example really tests Haskell-mode! Here is how the function is shown in the report.

showFloat x =
if x == 0 then showString ('0.' ++ take (m-1) (repeat '0'))
          else if e >= m-1 || e < 0 then showSci else showFix
where
showFix     = showString whole . showChar '.' . showString frac
              where (whole,frac) = splitAt (e+1) (show sig)
showSci     = showChar d . showChar '.' . showString frac
                               . showChar 'e' . showInt e
(m, sig, e) = if b == 10 then (w, s, n+w-1)
m'          = ceiling ((fromInt w * log (fromInteger b))/log 10) +1
(sig., e')  = if sig1 >= 10^m'         then (round (t/10), e1+1)
              else if sig1 < 10^(m'-1) then (sig1,      e1)
sig1        = round t
t           = s%1 * (b%1)^^n * 10^^(m'-e1-1)
e1          = floor (logbase 10 x)
(s, n)      = decodeFloat x
b           = floatRadix x
w           = floatDigits

This layout is not possible without manipulation, mainly due to the inconsistency of the alignment of the if statement. This is because if has to align with either then or else, this function uses both conventions. It is also necessary to come out an indentation level at certain points when writting the function (after an else or where) this is because the mode has to make a guess on where you want the next line to placed and does not get it right every time! Here is how the function looks in report-mode.

showFloat x =
    if x == o then showString ('0.' ++ take (m-1) (repeat '0'))
              else if e >= m-1 || e < 0 then showSci else showFix
    where
    showFix     = showString whole . showChar '.' . showString frac
                  where (whole,frac) = splitAt (e+1) (show sig)
    showSci     = showChar d . showChar '.' . showString frac
                           . showChar 'e' . showInt e
    (m, sig, e) = if b == 10 then (w, s, n+w-1)
    m'          = ceiling ((fromInt w * log (fromInteger b))/log 10) +1
    (sig, e')   = if sig1 >= 10^m'  then (round (t/10), e1+1)
                                    else if sig1 < 10^(m'-1) then (sig1, e1)
    sig1        = round t
    t           = s%1 * (b%1)^^n * 10^^(m'-e1-1)
    e1          = floor (logbase 10 x)
    (s, n)      = decodeFloat x
    b           = floatRadix x
    w           = floatDigits

4 Conclusion

4.1 Major Obstacles

There were a number of factors which were detrimental to the smooth and successful running of the project.

Initially, and indeed all the way through the project, the sheer size and complexity of Emacs Lisp proved to be a great barrier for several members of the group. Some of the members found Lisp to be a rather difficult language to pick up and use productively. Indeed, only one member of the group had previous experience with Lisp ^{3}. Many hours of implementation were discarded until the groups maturity with Emacs Lisp grew. In addition, a few members found the transition to Emacs itself to be problematic, perhaps preferring other text editors. Emacs being a large editor, with sometimes complicated commands, discouraged perseverance by the sheer volume of keystrokes the group had to commit to memory.

Another time consuming aspect of the project was the learning of Haskell’s syntax; finding out what was valid Haskell code and what was not. Haskell was new to one member of the group but the rest of the team had used Orwell, a very similar language to Haskell. Haskell is a moderately sized language and it provides many ways of saying the same thing. More to the point, humans develop their own styles and each style can be subtly different. These lead into the difficulty of actually writing Haskell-mode.

Predicting the indentation for a line of Haskell code is, at times, impossible. Sometimes, Haskell-mode can only guess at the indentation for the next line. It is not until a recognisable keyword is typed that the indentation can be authoritative. Realising this fact and developing the software around these ingrained difficulties was a trial-and-error exercise which consumed a lot of time and code. Many trade-offs had to considered which sometimes radically changed the direction of new approaches and significantly altered existing software.

3A dialect of Lisp, called Scheme.

4.2 Benefits Of Participation

The members of the team have become familiar with Emacs to varying degrees. Emacs is an industrially used text editor and previous experience with it may be an advantage during the transition from education to industry. With this knowledge of Emacs has come the knowledge of Lisp, which is a commonly used language in the field of Artificial Intelligence, especially in the United States. The fluent use of this language is a great advantage to those members of the group who are continuing with A.I. in the future.

The detailed study of Haskell syntax has deepened the group’s appreciation and understanding of the language itself, allowing group members to program in Haskell with confidence and competence.

In addition, the group has become aware of the different indentation styles adopted by people. Understanding indentation styles and, particularly, what makes a good style, is a research question in itself. The group has never tried to answer these complex questions; That is beyond the scope of this project. Instead, the team has looked at different styles for variations in format and similarities between styles. As much as possible, Haskell-mode was designed so it could be tailored by the user since any questions of “good” style were never addressed. The team became painfully aware of just how inconsistent people can be with indentation. This stressed that Haskell-mode needed to be a very flexible piece of software.

One of the most exciting benefits of the project has been the production of a working and useful piece of software. The group actively uses Haskell-mode when programming in Haskell.

An incidental skill picked up during the course of the project has been the learning of L^{A}TEX which is a very valuable document formatter.

The group has learned from first hand experience the trials and tribulations of team work and just how difficult it can be to overcome initial and continuing hurdles which present themselves before the completion of a substantial software project.

4.3 Assessment

Looking back over the course of the project there are many areas which could and should have been handled differently.

There was a motivational issue to be overcome for some of the group members and this posed a problem all through the project. The difficulties the project presented took time and perseverance to overcome. More group meetings would, in retrospect, have been a good idea (the group met around once every two–three weeks and on an informal basis).

Regular contact with the project supervisor, although not really essential for this project, may have helped to keep the team focussed and on schedule. In general, the project was behind schedule.

Due to these difficulties, some of the initially planned features of the software could not be included due to time constraints.

5 The Summary Log

In the log, we summarise the general progress made from December 1992 to February 1993 and then move onto the more detailed analysis of design and implementation.

In December 1992–January 1993 the group undertook to learn Emacs Lisp. Some of the members of the group, who had no previous experience with Emacs as a text editor, had to become acquainted with it. Emacs Lisp was learned by reading through [3] and the accompanying tutorial. This took a considerable amount of time for some of the group members, especially those with no prior experience of Lisp or Emacs. Emacs Lisp is a large language and a lot of experience is required before serious coding can take place.

From mid January to the end of the month, the group set out to construct a design plan and consider the requirements of the project. The team learned Haskell basically from [2] which proved to be a dense and, at times, confusing document. The team identified the more common indentation styles of Haskell code and considered, within the context of Emacs Lisp, the different variables which could help tailor the indentation of programs to better suit the individual.

In the first week of February the team met with Kevin Hammond, the project supervisor, to discuss a design plan which provided a timetable for the completion of the various stages of the project. A specification, which dealt with the kind of facilities Haskell mode would provide, was drafted.

In the second week, the team met again with Kevin to look over the documents and discuss the progression of the project. A working prototype was scheduled for release, along with a user manual, for the end of March. The group undertook to learn L^{A}TEX for formatting the user manual.

In the third week, Richard worked on the design and the way Haskell-mode should deal with indentation. This week’s work was discarded as the way the major mode indented was poor.

The fourth week saw Richard working on the automatic insertion of comments and the partial implementation of the where clause. This was discovered to be a big topic with many stylistic possibilities.

In the first week of March, still more work was done on the where clause and function declarations. It became obvious that Haskell-mode had to have some way of knowing when a function definition had come to an end. This was remedied by requiring that functions be separated by blank lines. This had the side effect that blank lines could not used within functions.

At the beginning of the next week the group met with Kevin who discussed the approach taken so far. He made several suggestions which Richard realised would dramatically change the direction of the project for the better. Richard implemented these suggestions to Haskell-mode which made the program more correct. Kevin’s knowledge of Haskell proved to be a great benefit at this meeting.

During the following week, Richard tested and debugged the code which had been written to show errors in the code rather than errors in the design. More work was done on the where clause and more alternative styles were catered for. Richard’s experience with both Haskell and Emacs Lisp were becoming greater and so revisions of earlier, less mature, coding had to be performed.

The following two weeks saw a little less work due to other academic commitments, but Richard still dedicated a considerable amount of time to the if and case statements. A little work was done on the let expression. By the end of this period, the group had initially wanted to release a prototype but Richard felt that would be unwise since the quality of the product was not high enough to entrust it with other peoples files! The work load carried by some members of the group had dropped considerably through the last month. Andrew continued to test the code with examples and notify any problems to Richard. Richard noticed that more contextual analysis was needed to correctly determine where to position the line being indented.

From April 1st. the let expression was better implemented but, as with the if statement, was still not perfect. Richard became aware that the initially intended compiler interaction in the major mode could now not be implemented due to the lack of time. Testing continued which highlighted the work still to be done. During the spring vacation, it was very difficult for the team to meet regularly and a certain degree of contact was lost.

Over the next few weeks the main topic which Richard implemented was the continuation of bracketed expressions over multiple lines. This proved to be a very frustrating task. Richard wrote a lot of code, which was very time consuming, without knowing that Emacs provided a function which suited his needs almost perfectly. (The poorly documented function in question was “parse-partial-sexp”). Indeed, this exemplified how a lot of time and effort was lost during the course of the project. Richard took heart because he was certainly becoming far more knowledgeable and mature in writing Emacs Lisp code.

The testing continued constantly and Andrew reported all errors and suggestions to Richard. Charlie and Scott began to look at what still needed to be done; the let and if expressions needed to be refined which they undertook. Richard gave guidance since both their knowledge of Emacs Lisp was a little less than his.

In the next week, the group met with Kevin to discuss what documents should be produced for the final report. Richard saw the need to implement a function (haskellfurther-indent) which would allow the user to indent the current line in order to continue function applications over several lines. Currently, the major mode only allowed parenthesised expression to spill over lines. Richard began work on the user manual and also continued work on debugging the existing code. All the debugging throughout the project was done using the Emacs Lisp debugger (debug-on-entry-to-function) which proved to be a very useful tool.

During the next week, the group havd to write the bulk of the report. Richard continued work on the software, finishing implementation of the let and if statements. He also decided to implement a function which would bring the user out of an indentation level (haskell-lesser-indent). This was especially useful for when nested declarations occur. Previously, the assumption had been that the user would manually alter the indentation. With the implementation of this function came the realisation that the major function haskellcalculate-indentation would have to be recursive to better suit contextual analysis. This led to Richard implementing the haskell-context-indent function. Final testing was performed using more complicated examples. The software was released to Haskell programmers with the computing department here at Glasgow which helped expose the limitations of Haskellmode. The first implementation of haskell-lesser-indent proved to be far to slow. Richard analysised the problem and noticed just how inefficient the algorithm was. He altered the function and a more acceptable time for execution of the function was acquired. During testing, it became clear that the previous assumption of letting blank lines terminate functions was very limiting due to the result that blank lines could not be inserted in function declarations. Many people like having blank lines to separate long functions. This resulted in the trade off that the way to begin a new function is to press return rather than line-feed at the end of a function, so that the cursor is positioned at the right hand edge. Also, the user needs to press return after this first line or else the line may be re-indented under the scope of the last function.

During the final week, the group tested the software and finished the report. Charlie and Scott started on the background and conclusions respectively. Andrew and Scott wrote section 3. Richard extended the background and conclusion sections and finished work on the user manual. He wrote the log and status report.

6 Status Report

6.1 Limitations and Known Bugs

Haskell-mode is an operational and useful tool for any Haskell programmer. It has been tested extensively and the team has confidence in its ability to successfully indent Haskell code.

Having said that, there are a few deficiencies and limitations in the software which need to be discussed.

1. Sometimes, even when the indentation seems clear to the user, Haskell-mode will place the cursor in a seemingly awkward position. Do not worry. Haskell-mode needs to see a keyword sometimes before it can be sure of the line’s indentation. Do not move the cursor manually until you type the line and press line-feed or tab.

2. The function haskell-lesser-indent (C-c b) can have difficulty in moving out of an indentation if more than one statement occurs on the one line. For example,

     foo a = let b = if a == 1 then 5
                               else 7
     !                         {C-c b}

The cursor is positioned as illustrated by the !. If the user wished to continue declarations under the scope of the let, then the only way to remedy this situation is to use the space and delete keys to indent the line.

3. The indent-region function is of limited use since it is common to override indentation by use of the commands C-c b and C-c f. The function re-indents those lines which have been altered in any way.

4. Haskell-mode does a lot of work to find the indentation for a line and so there is a noticeable (one second) delay before the line is indented.

In addition to these problem areas, Haskell-mode failed to provide these useful facilities:

1. Interaction with a Haskell compiler. Due to time constraints, the compilation features, which would have allowed easy compilation of Haskell programs within Emacs, had to be omitted.

2. Indentation facility for literate scripts. The team would have liked to implement this feature which would allow Haskell-mode to indent a text file whose lines begin with “>” as Haskell-code and any other line would be ignored. The team was not aware of this feature as a possibility for the system.

6.2 Testing

The major mode was tested extensively as it was built and the list of shortcomings was noted above. The mode was tested on various indentation possibilities. Emacs provides the function “eval” which allows the user to evaluate a specific function. This function was used extensively during testing to test each individual function with various text in the buffer. Testing was performed in cases where the function would be taken to its “limits”; Examples being when the function would have to deal with the beginning or end of a buffer, or when the buffer was empty. This individual analysis of function was very valuable in ironing out bugs.

On a heavily loaded server, Haskell-mode was noticeably slow (about one second to perform indentation). This problem was noted for larger Haskell programs. With this in mind, the inefficient areas of code were tracked down and rectified to produce a better product.

Where problems arose, they were reported and rectified or noted in the list of shortcomings. Major alterations were made as it was seen that assumptions such as blank lines indicating the end of a function were somewhat poor. Testing showed up the need for the commands “further-indent” and “lesser-indent”. The testing also revealed where the program was deficient. The testing was done with both simple and more complicated examples found in books and by actually using the major mode whilst programming generally in Haskell. This gave the creators proper hands-on experience of being a user of the system, giving them a much better idea of what was required.

A HASKELL-MODE USER MANUAL 25

A Haskell-Mode User Manual

This section has been temporarily removed.

B THE EMACS LISP CODE FOR HASKELL-MODE 26

B The Emacs Lisp Code for Haskell-Mode

In this appendix, we present the code for Haskell-mode written in Emacs Lisp. This is to appear soon...

References

[1] John Hughes, Simon Peyton Jones, Philip Wadler. SERC Research Grant Final Report (GR/E 85799): The FPLANG Committee: Establishing a lazy functional language, February 1988 – August 1989

[2] Paul Hudak, Simon Peyton Jones, Philip L. Wadler, Brian Boutel, Jon Fairbairn, Joseph H. Fasel, Maria M. Guzman, Kevin Hammond, John Hughes, Thomas Johnsson, Dick Kieburtz, Rishiyur Nikhil, and John Peterson. Report on the programming language haskell: a non-strict, purely functional language, version 1.1. Technical Report YALEU/DCS/RR-777, Yale University Department of Computer Science, August 1991.

[3] Robert Krawitz, Bil Lewis, DanLiberte, Richard M. Stallman, and Chris Welty. GNU Emacs Lisp Reference Manual, edition 1.03. (electronic) 1988 Free Software Foundation, Inc.