Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Getting started with Haskell programming

Introducing functional programming introduced the foundations of functional programming in Haskell. We are now ready to use GHCi to do some practical programming, and we introduce it here.

In beginning to program we will also learn the basics of the Haskell module system, under which programs can be written in multiple, interdependent files, and which can use the ‘built-in’ functions in the prelude and libraries. Our programming examples will concentrate on using the Picture example introduced in Introducing functional programming as well as some simple numerical examples.

In support of this we will look at how to get hold of the programs and other background materials for the book from Hackage using Cabal, as well as how to obtain and install GHCi using GHCup. We will explore how to develop single-module programs using ghci as well as using cabal repl for more complex, multiple module projects.

We conclude by briefly surveying the kinds of error message that can result from typing something incorrect into GHCi.

{- #########################################################
        FirstScript.hs
        Simon Thompson, August 2010.
######################################################### -}

module FirstScript where

--      The value size is an integer (Integer), defined to be 
--      the sum of twelve and thirteen.

size :: Integer
size = 12+13

--      The function to square an integer.

square :: Integer -> Integer
square n = n*n

--      The function to double an integer.
        
double :: Integer -> Integer
double n = 2*n

--      An example using double, square and size.
         
example :: Integer
example = double (size - square (2+2))

An example script, `FirstScript.hs`.

A first Haskell program

We begin the chapter by giving a first Haskell program or script, which consists of the numerical examples from Introducing functional programming. This is the file FirstScript.hs, shown in An example script, FirstScript.hs.. Haskell scripts are stored in files with the extension.hs’.

As well as definitions, a script will contain comments. A comment in a script is a piece of information of value to a human reader rather than to a computer. It might contain an informal explanation of how a function works, how it should or should not be used, the overall design philosophy of a library and so on. Everything in a program file is interpreted as program text, except where it is explicitly indicated that it is a comment.

Comments are indicated in two ways. The symbol ‘--’ begins a comment which occupies the part of the line to the right of the symbol. Comments can also be enclosed by the symbols ‘{-’ and ‘-}’. These comments can be of arbitrary length, spanning more than one line, as well as enclosing other comments; they are therefore called nested comments.

Using Haskell in practice

The Glasgow Haskell Compiler (GHC) is an industrial-strength compiler for Haskell. GHC interactive (GHCi) is an interactive interpreter based on GHC, which provides programmers with a Haskell repl, which successively reads an expression, evaluates it, prints the result, and finally loops back to the start. This is a great way to start programming, as it allows you to try out your ideas and experiment with definitions in the process of developing a program.

If you’re learning Haskell in high school or college, or at work, your teachers or colleagues will have made sure it is available on the computers you use. If you want to install it for yourself on a machine that you control, the best, and easiest, way of working doing that is to use GHCup, https://www.haskell.org/ghcup/.

The GHCup homepage

GHCup will install GHC, as well as some other components including cabal, which is a package for distributing Haskell code and libraries, and one of the ways that you can access the code for the book;. If asked to select what to download, include cabal, and if you want to use Haskell within an IDE like VS Code, include the Haskell Language Server (HLS) too. Also, you should follow the GHCup default of installing the recommended versions of these programs.

Why should you consider using HLS? Development today is usually inside an Integrated Development Environment, or IDE, such as Visual Studio Code (VS Code). IDEs combines editing with processing and running programs, as well as interacting with reposito- ries, such as github, refactoring and other kinds of software tooling. To use Haskell most effectively within an IDE you will need to use the Haskell Language Server (HLS), which provides a rich enhancement of the editing functions of the IDE including type checking, refactoring, integration with repositories and other support for software development.

We will come back to cabal and HLS when we discuss multi-file projects in Working with multiple-module projects and again when we discuss how to find out more about the functions and libraries in Haskell in Programming with lists.

Other ways of using Haskell – such as using it online or in a browser – can be found in Appendix Haskell practicalities.

A GHCi terminal session

Using GHCi

In this text we describe the terminal-style interface to GHCi, illustrated in A GHCi terminal session, because this is common to macOS, Linux and Windows WSL.

GHCi documentation

The GHCup webpage has documentation about ‘first steps’ in getting started, covering ghci after a short introduction to the ghc command itself.

https://www.haskell.org/ghcup/steps/

The full documentation for the Glasgow Haskell Compiler is here

https://downloads.haskell.org/ghc/latest/docs/users_guide/

and that includes information specifically about GHCi.

Starting GHCi

To start GHCi in a terminal on macOS, Linux and WSL, type ghci to the prompt in a terminal. To launch GHCi using a particular file, change to the directory containing the file and type ghci followed by the name of the file in question, as in

ghci Chapter2

Haskell scripts carry the extension .hs; only such files can be loaded, and their extensions can be omitted when they are loaded either when GHCi is launched or by a :load command within GHCi.

Evaluating expressions in GHCi

As we said in Expressions and evaluation, GHCi will evaluate expressions typed at the prompt. We see in A GHCi terminal session the evaluation of double (square 29) to 1682, like this

ghci> double (square 29)
1682
ghci>

where we have indicated the machine output by using a slanted font; user input appears in unslanted form. The prompt here, ghci>, will be explained in Modules below. As can be seen from the examples, we can evaluate expressions which use the definitions in the current script. In this case it is Chapter2.hs.

One of the advantages of the GHCi repl interface is that it is easy to experiment with functions, trying different evaluations simply by typing the expressions at the keyboard. If we want to evaluate a complex expression, we could add it to the program, as in this definition

cube :: Integer -> Integer
cube n = n*n*n

but we can also input these definitions directly into GHCi by prefacing them with the keyword let, as seen in A GHCi terminal session. Of course, this definition will be lost when we close GHCi, so if we want to keep a record of it, then we should add it to a Haskell module as well.

CommandAbbrev.Action
 
:load Parrot:lLoad the Haskell module Parrot.hs; the file extension .hs can be omitted.
:reload:rRepeat the last :load command.
:type exp:tGive the type of the expression exp; e.g. typing :type size+2  gives  size+2 :: Integer.
:info name :iGive information about the thing called name.
:browse Name Give information about the definitions in the module Name, if it is loaded.
:quit:qQuit the system.
:help:h,:?Give a complete list of the GHCi commands.
:! commandEscape to perform a Unix or DOS command.
:edit First.hs :eEdit the file First.hs in the default editor. Note that the file extension .hs is needed in this case. See the following section for more information on editing.
:set editor vi :sSet the editor to be vi.
, Move up () and down () the command history.
Name and command completion: complete module or file names, or GHCi commands.
let s = expGive s the value of exp within this GHCi session.

Principal GHCi commands

GHCi commands

GHCi commands begin with a colon, ‘:’. A summary of the main commands is given in Principal GHCi commands. When a GHCi command can be abbreviated to their initial letter this is shown in the table in Principal GHCi commands. Outline information about other commands is given by the :help command, and comprehensive details can be found in the on-line GHCi documentation discussed above.

Editing scripts

GHCi can be connected to a default text editor, so that GHCi commands such as :edit use this editor. This may well be determined by your local set-up (e.g. in the EDITOR variable), or can be set using the :set command in GHCi.

Using the GHCi :edit command causes the editor to be invoked on the appropriate file. When the editor is quit, the updated file is loaded automatically. However, it can be more convenient to keep the editor running in a separate window and to reload the file by:

  • writing the updated file from the editor (without quitting it), and then

  • reloading the file in GHCi using :reload or :reload filename.

In this way the editor is still open on the file should it need further modification.

A first GHCi session

Let’s get started with GHCi by doing some introductory exercises.

Task 1

Load the file FirstScript.hs into GHCi, and evaluate the following expressions

square size
square
double (square 2)
it
square (double 2)
let d = double 2
square d
23 - double (3+1)
23 - double 3+1
it + 34
13 `div` 5
13 `mod` 5

On the basis of this can you work out the purpose of it and let?

Task 2

Use the GHCi command :type to tell you the type of each of these, apart from it.

Task 3

What is the effect of typing each of the following?

double 2 3
double square
2 double

Try to give an explanation of the results that you obtain.

Task 4

Edit the file FirstScript.hs to include definitions of functions from integers to integers which behave as follows.

  • The function should double its input and square the result of that.

  • The function should square its input and double the result of that.

Your solution should include declarations of the types of the functions.

let in GHCi

It is possible to make temporary definitions in GHCi using let like this:

let s = 23

and once you have done this you can use s in any expressions you evaluate. You can define any Haskell value this way, too.

Beware! These definitions are lost if you redefine s or when you leave GHCi, so it often best to put definitions into a file, so that they are saved and you can edit them subsequently.

The standard prelude and the Haskell libraries

We saw in Introducing functional programming that Haskell has various built-in types, such as integers and lists and functions over those types, including the arithmetic functions and the list functions map and ++. Definitions of these are contained in a file, the standard prelude, Prelude.hs. When Haskell is used, the default is to load the standard prelude, and this can be seen by trying the GHCi command

:browse Prelude

which will list the types of all the functions in the Prelude.hs module.

As Haskell has developed over the last decade, the prelude has also grown. In order to make the prelude smaller, and to free up some of the names used in it, many of the definitions have been moved into standard libraries, which can be included when they are needed. We shall say more about these libraries as we discuss particular parts of the language.

As well as the standard libraries, there is a wealth of contributed libraries that support property-based testing, concurrency, functional animations and so forth; we will introduce the libraries that we need as we go along. These libraries are available on Hackage, https://hackage.haskell.org, the main online package repository for Haskell, using the Cabal installation system. We will say something more about Cabal in Working with multiple-module projects and give an overview of more Haskell modules and libraries in Programming with lists.

Modules

GHCi, the Prelude and other modules

A typical piece of computer software will contain thousands of lines of program text. To make this manageable, we need to split it into smaller components, which we call modules.

A module has a name and will contain a collection of Haskell definitions. To introduce a module called Ant we begin the program text in the file thus:

module Ant where
   ...

A module may also import definitions from other modules. The module Bee will import the definitions in Ant by including an import statement, thus:

module Bee where
import Ant
   ...

The import statement means that we can use all the definitions in Ant when making definitions in Bee. In dealing with modules in this text we adopt the conventions that

  • there is exactly one module per file;

  • the file Blah.hs contains the module Blah.

The module mechanism supports the libraries we discussed in The standard prelude and the Haskell libraries, but we can also use it to include code written by ourselves or someone else.

The module mechanism allows us to control how definitions are imported and also which definitions are made available or exported by a module for use by other modules. We look at this in more depth in Case study: Huffman codes, where we also ask how modules are best used to support the design of software systems.

In the light of what we have seen so far, GHCi, the Prelude and other modules illustrates a GHCi session. like this: The current module will have access to the standard prelude, and to those modules which it imports; these might include modules from the standard libraries, which are found in the same directory as the standard prelude. The user interacts with GHCi, providing expressions to evaluate and other commands and receiving the results of the evaluations. In the next section we look at how to work with multiple-module projects in GHCi, including the code for this text.

Working with multiple-module projects

As we saw in the previous section, Haskell projects can be built from multiple modules, using the import and export mechanism. In this section we look at how projects can be downloaded and used; in particular how we can make sure that we’re able to resolve all the dependencies of a particular project. We will see this in action with the Haskell package for this book, Craft3e.

Haskell projects, most of which consist of multiple modules, can be found on the Haskell Package Manager site, Hackage, and downloaded using Cabal. These projects themselves can depend on other packages, and this dependency and other project metadata is contained in a .cabal file in the home directory of the package. The .cabal file summarises this information in a machine-readable format, allowing GHC to load all the dependencies of the project.

Working with Craft3e

The code for this text is available on Hackage at

https://hackage.haskell.org/package/Craft3e

To download this to your machine type cabal unpack Craft3e. This will install the code for the book in a folder under .cabal in your home directory: the folder will be named Craft3e-<version>, where <version> is replaced by the current version number, 0.2.0.4 at the time of writing (2026-09-03).

Alternatively, if you are familiar with git and github, you can obtain the code from the github repository

https://github.com/simonjohnthompson/haskellcraft

by cloning the repository. As well as the code, this repository contains the source code for the book itself, and other miscellaneous material.

Whichever way you obtain the project, to work with the project, first change directory into the directory Craft3e (if you cloned the project from github) or Craft3e-<version> if you used cabal unpack. In either case you can then first run

cabal build  

which will install all the code that the book depends on; you just need to do this once, after obtaining the Craft3e project. Then to interact with the code in the project type

cabal repl

This gives you a ghci prompt, but this time connected to GHC with all the dependencies available. You can work with ghci in the usual way. The first time that you run cabal repl after obtaining the project may take time to complete, as the system installs dependencies: don’t worry, this won’t be a problem the next time you use it.

Any new modules, such as MyNewModule.hs that you create within or below the Craft3e directory will also have these dependencies available. These can be loaded with :l MyNewModule, and reloaded with :r, in the usual way.

If you choose to use an IDE such as VS Code you should open the Craft3e folder using the Open Folder command in the File menu. This will ensure that dependencies are visible, and the built-in terminal will be opened in the same directory. You can then use the Cabal commands just as they are described above. VS Code is enhanced with HLS using the haskell.haskell extension, which is documented and downloadable from here:

https://marketplace.visualstudio.com/items?itemName=haskell.haskell

The next section revisits the picture example of Introducing functional programming, which is used to give a practical illustration of modules.

module Pictures where

type Picture = ....

-- The horse example used in Craft3e, and a white picture.

horse , white :: Picture
horse = ....
white = ....

-- Getting a picture onto the screen.

printPicture :: Picture -> IO ()
printPicture = ....

-- Reflection in vertical and horizontal mirrors.

flipV , flipH :: Picture -> Picture
flipV = map reverse
flipH = reverse

-- One picture above another. To maintain the rectangular 
-- property, the pictures need to have the same width.

above :: Picture -> Picture -> Picture
above = (++)

-- One picture next to another. To maintain the rectangular 
-- property, the pictures need to have the same height.

beside :: Picture -> Picture -> Picture
beside = zipWith (++)

-- Superimpose two pictures (assumed to be same size). 

superimpose :: Picture -> Picture -> Picture
superimpose = ....

-- Invert the black and white in the picture.

invertColour :: Picture -> Picture
invertColour = ....
A view of the `Pictures` module.

A second example: pictures

The running example in Introducing functional programming was of pictures, and we saw there that there are two implementations of these functions, one in Pictures.hs giving an ‘ASCII art’ version, and the other in PicturesSVG.hs rendering pictures in a web browser.

  • To use PicturesSVG.hs, open GHCi on this module like this:

    ghci PicturesSVG.hs
    

    To show a picture in the browser, evaluate it using render as in

    render (horse `beside` (flipV horse))
    

    This will give a browser display as shown in Viewing Pictures in a web browser. The image in the browser will update automatically when you call render on another picture; if you would prefer to do this manually, use the file showPic.html instead.

  • To use the ‘ASCII art’ version, run

    ghci Pictures.hs
    

    To show a picture in the terminal you need to use the function

    printPicture :: Picture -> IO ()
    

    which is used to display a Picture on the screen. The type IO is a part of the Haskell mechanism for input/output (I/O). We examine this mechanism in detail in Playing the game: I/O in Haskell; for the present it is enough to know that if horse is the name of the picture used in the earlier examples, then the effect of the function application  printPicture horse  is the display

    .......##... 
    .....##..#..
    ...##.....#.
    ..#.......#.
    ..#...#...#.
    ..#...###.#.
    .#....#..##.
    ..#...#.....
    ...#...#....
    ....#..#....
    .....#.#....
    ......##....
    

    first seen in Introducing functional programming. Any Picture can be printed in a similar way. The Pictures module is shown in A view of the Pictures module..

In the remainder of this section we present a series of practical exercises designed to use either of the modules Pictures.hs and PicturesSVG.hs.

Exercises

2.1 Define a module UsePictures which imports Pictures (or PicturesSVG) and contains definitions of blackHorse and rotateHorse which can use the definitions imported from the pictures module.

In the remaining questions you are expected to add other definitions to your module UsePictures.

2.2 How could you make the picture

Try to find two different ways of getting the result. It may help to work with pieces of white and black paper.

Using your answer to the first part of this question, how would you define a chess (or checkers) board, which is an 8×8 board of alternating squares?

2.3 Three variants of the last picture which involve the ‘horse’ pictures are

How would you produce these three?

2.4 Give another variant of the ‘horse’ pictures in the previous question, and show how it could be created. Note: a nice variant is

Errors and error messages

No system can guarantee that what you type is sensible, and GHCi is no exception. If something is wrong, either in an expression to be evaluated or in a script, you will receive an error message. Try typing

2+(3+4

to the GHCi prompt. The error here is in the syntax, and is like a sentence in English which does not have the correct grammatical structure, such as ‘Fishcake our camel’.

The expression has too few parentheses: after the ‘4’, a closing parenthesis is expected, to match with the opening parenthesis before ‘3’. The error message says that something is wrong, but in fact it’s not to do with indentation, but rather the lack of a closing parenthesis:

<interactive>:1:7: error: [GHC-58481]
    parse error (possibly incorrect indentation or mismatched brackets)

In a similar way typing 2+(3+4)) results in the message

<interactive>:2:8: error: [GHC-58481] parse error on input ‘)’

which this time says that (one of) the closing parentheses causes a problem. Now try typing the following expression.

double square

This gives a type error, since double is applied to the function square, rather than an integer:

<interactive>:5:1: error: [GHC-39999]
    • No instance for ‘Show (Integer -> Integer)’
        arising from a use of ‘print’
        (maybe you haven't applied a function to enough arguments?)
    • In a stmt of an interactive GHCi command: print it

The message indicates that the system is trying to print something of type Integer -> Integer, that is a function from integers to integers. It’s a bit confused, but says that maybe a function has been applied to too few arguments. That’s not the case here, but it might point us to the fact that there’s an incorrect function application here: double has been applied to a function rather than a number.

When you get an error message like the one above you need to look at how the term, in this case square of type Integer -> Integer, does not match the context in which it is used: the context is given in the second line (double square) and the type required by the context, Integer, is given in the last line.

Type errors do not always give rise to such well-structured error messages. Typing either 4 double or 4 5 will give rise to a message like

<interactive>:6:1: error: [GHC-39999]
    • Could not deduce ‘Num a0’
      from the context: (Num a, Num ((a -> a) -> t))
        bound by the inferred type for ‘it’:
                   forall {a} {t}. (Num a, Num ((a -> a) -> t)) => t
        at <interactive>:6:1-8
      The type variable ‘a0’ is ambiguous
      Potentially matching instances:
        instance Num Integer -- Defined in ‘GHC.Num’
        instance Num Double -- Defined in ‘GHC.Float’
        ...plus three others
        ...plus one instance involving out-of-scope types
        (use -fprint-potential-instances to see them all)
    • In the ambiguity check for the inferred type for ‘it’
      To defer the ambiguity check to use sites, enable AllowAmbiguousTypes
      When checking the inferred type
        it :: forall {a} {t}. (Num a, Num ((a -> a) -> t)) => t

We will explore the technical details behind these messages in a later chapter; for now it is sufficient to read these as ‘Type Error!’. One thing we can focus on, though, is the place that it says the error occurs: suppose that this was inside a larger program, it would still indicate the appearance of 4 double as giving rise to the problem. So, always take note of where an error is said to occur.

The last kind of error we will see are program errors. Try the expression

4 `div` (3*2-6)

We cannot divide by zero (what would the result be?) and so we get the message

*** Exception: divide by zero

indicating that a division of 4 by 0 has occurred. More details about the error messages produced by GHCi can be found in Appendix GHCi errors.

Summary

The main aim of this chapter is practical, to acquaint the reader with the GHCi implementation of Haskell. We have seen how to write simple Haskell programs, to load them into GHCi and then to evaluate expressions which use the definitions in the module.

Larger Haskell programs are structured into modules, which can be imported into other modules. Modules support the Haskell library mechanism and we illustrate modules in the case study of Pictures introduced in Introducing functional programming.

We concluded the chapter with an overview of the possible syntax, type and program errors in expressions or scripts submitted to GHCi.

The first two chapters have laid down the theoretical and practical foundations for the rest of the book, which explores the many aspects of functional programming using Haskell and GHCi.