Personal tools

How to write a Haskell program

From HaskellWiki

(Difference between revisions)
Jump to: navigation, search
(Build system)
Line 26: Line 26:
For libraries, use [ Haddock]. We recommend
For libraries, use [ Haddock]. We recommend
using the latest version of Haddock.
using the version of Haddock that ships with the Haskell Platform. Haddock generates [ nice markup], with links to source.
=== Testing ===
=== Testing ===

Revision as of 17:42, 26 April 2011

A guide to creating a new Haskell project or program.


1 Recommended tools

Almost all new Haskell projects use the following tools. Each is intrinsically useful, but using a set of common tools also helps everyone by increasing productivity, and you're more likely to get patches.

1.1 Revision control

Use git or darcs unless you have a specific reason not to. Both are lightweight distributed revision control systems (and darcs is written in Haskell). Both have massive market share in the Haskell world, if you want to encourage contributions from other Haskell hackers git or darcs are the best. Darcs hosting is available on and patch-tag. github for git is very popular.

1.2 Build system

Built with Cabal


You should use cabal-install as a front-end for install your Cabal library. Cabal-install provides commands not only for building libraries but also for installing them from, and uploading them to, Hackage. As a bonus, for almost all programs, it's faster than using Setup.hs scripts directly, since no time is wasted compiling the scripts. (This does not apply for programs that use custom Setup.hs scripts, since those need to be compiled even when using cabal-install.)

cabal-install is widely available, as part of the Haskell Platform, so you can probably assume your users will have it too.

1.3 Documentation

For libraries, use Haddock. We recommend using the version of Haddock that ships with the Haskell Platform. Haddock generates nice markup, with links to source.

1.4 Testing

You can use QuickCheck or SmallCheck to test pure code. To test impure code, use HUnit.

To get started, try Introduction to QuickCheck. For a slightly more advanced introduction, Simple Unit Testing in Haskell is a blog article about creating a testing framework for QuickCheck using some Template Haskell. For HUnit, see HUnit 1.0 User's Guide

1.5 Distribution

The standard mechanism for distributing Haskell libraries and applications is Hackage. Hackage can host your cabalised tarball releases, and link to any library dependencies your code has.

2 Structure of a simple project


  • Haq.hs -- the main haskell source file
  • haq.cabal -- the cabal build description
  • Setup.hs -- build script itself
  • _darcs -- revision control
  • README -- info
  • LICENSE -- license

Of course, you can elaborate on this, with subdirectories and multiple modules. See Structure of a Haskell project for an example of a larger project's directory structure.

Here is a transcript that shows how you'd create a minimal darcs and cabalised Haskell project for the cool new Haskell program "haq", build it, install it and release.

The new tool "cabal init" automates all this for you, but you should understand all the parts even so.


2.1 Create a directory

Create somewhere for the source:

$ mkdir haq
$ cd haq

2.2 Write some Haskell source

Write your program:

$ cat > Haq.hs
-- Copyright (c) 2006 Don Stewart -
-- GPL version 2 or later (see
import System.Environment
-- | 'main' runs the main program
main :: IO ()
main = getArgs >>= print . haqify . head
haqify s = "Haq! " ++ s

2.3 Stick it in darcs

Place the source under revision control (you may need to enter your e-mail address first, to identify you as maintainer of this source):

$ darcs init
$ darcs add Haq.hs 
$ darcs record
addfile ./Haq.hs
hunk ./Haq.hs 1
+-- Copyright (c) 2006 Don Stewart -
+-- GPL version 2 or later (see
+import System.Environment
+-- | 'main' runs the main program
+main :: IO ()
+main = getArgs >>= print . haqify . head
+haqify s = "Haq! " ++ s
What is the patch name? Import haq source
Finished recording patch 'Import haq source'

And we can see that darcs is now running the show:

$ ls
Haq.hs _darcs

2.4 Add a build system

Create a .cabal file describing how to build your project:

$ cat > haq.cabal
Name:                haq
Version:             0.0
Description:         Super cool mega lambdas
License:             GPL
License-file:        LICENSE
Author:              Don Stewart
Build-Type:          Simple
Cabal-Version:       >=1.2
Executable haq
  Main-is:           Haq.hs
  Build-Depends:     base >= 3 && < 5

(If your package uses other packages, e.g. haskell98, you'll need to add them to the Build-Depends: field as a comma separated list.) Add a Setup.hs that will actually do the building:

$ cat > Setup.hs
import Distribution.Simple
main = defaultMain

Cabal allows either Setup.hs or Setup.lhs.

Now would also be a good time to add a LICENSE file and a README file. Examples are in the tarball for HNop.

Record your changes:

$ darcs add haq.cabal Setup.hs LICENSE README
$ darcs record --all
What is the patch name? Add a build system
Finished recording patch 'Add a build system'

2.5 Build your project

Now build it! There are two methods of accessing Cabal functionality: through your Setup.hs script or through cabal-install. In most cases, cabal-install is now the preferred method.

Building using cabal-install:

$ cabal install --prefix=$HOME --user

Building using the traditional Setup.hs method:

$ runhaskell Setup configure --prefix=$HOME --user
$ runhaskell Setup build
$ runhaskell Setup install

This will install your newly minted haq program in $HOME/bin.

2.6 Run it

And now you can run your cool project:

$ haq me
"Haq! me"

You can also run it in-place, even if you skip the install phase:

$ dist/build/haq/haq you
"Haq! you"

2.7 Build some haddock documentation

Generate some API documentation into dist/doc/*

Using cabal install:

$ cabal haddock

Traditional method: $ runhaskell Setup haddock

which generates files in dist/doc/ including:


haq Contents Index
main :: IO ()
main :: IO ()
main runs the main program
Produced by Haddock version 0.7

No output? Make sure you have actually installed haddock. It is a separate program, not something that comes with Cabal. Note that the stylized comment in the source gets picked up by Haddock.

2.8 (Optional) Improve your code: HLint

HLint can be a valuable tool for improving your coding style, particularly if you're new to Haskell. Let's run it now.

$ hlint .
./Haq.hs:11:1: Warning: Eta reduce
  haqify s = "Haq! " ++ s
Why not:
  haqify = ("Haq! " ++)

The existing code will work, but let's follow that suggestion. Open Haq.hs in your favourite editor and change the line:

    where haqify s = "Haq! " ++ s


    where haqify = ("Haq! " ++)

2.9 Add some automated testing: QuickCheck

2.9.1 QuickCheck v1


$ cat > Tests.hs
import Char
import List
import Test.QuickCheck
import Text.Printf
instance Arbitrary Char where
    coarbitrary c = variant (ord c `rem` 4)

Now let's write a simple property:

$ cat >> Tests.hs 
    where _ = s :: [Int]
-- and add this to the tests list


$ runhaskell Tests.hs
reverse.reverse/id       : OK, passed 100 tests.

Let's add a test for the 'haqify' function:

    where haqify s = "Haq! " ++ s
        ,("drop.haq/id",        test prop_haq)]

and let's test that:

$ runhaskell Tests.hs
reverse.reverse/id       : OK, passed 100 tests.
drop.haq/id              : OK, passed 100 tests.


2.9.2 QuickCheck v2

If you're using version 2 of QuickCheck, the code in the previous section needs some minor modifications:

$ cat > Tests.hs
import Char
import List
import Test.QuickCheck
import Text.Printf
    where _ = s :: [Int]
    where haqify s = "Haq! " ++ s
tests  = [("reverse.reverse/id", quickCheck prop_reversereverse)
        ,("drop.haq/id",        quickCheck prop_haq)]

To run the test:

$ runhaskell Tests.hs
reverse.reverse/id       : +++ OK, passed 100 tests.
drop.haq/id              : +++ OK, passed 100 tests.


2.10 Running the test suite from darcs


$ darcs setpref test "runhaskell Tests.hs"
Changing value of test from  to 'runhaskell Tests.hs'

will run the full set of QuickChecks. If your test requires it, you may need to ensure other things are built too -- for example:darcs setpref test "alex Tokens.x;happy Grammar.y;runhaskell Tests.hs". You will encounter that this way a darcs patch is also accepted if a QuickCheck test fails. You have two choices to work around this:

  • Use
    from the package QuickCheck-2 and call
    if it return
  • Keep the test program as it is, and implement the failure on the shell level:
runhaskell Tests.hs | tee test.log && if grep Falsifiable test.log >/dev/null; then exit 1; fi

Let's commit a new patch:

$ darcs add Tests.hs
$ darcs record --all
What is the patch name? Add testsuite
Running test...
reverse.reverse/id       : OK, passed 100 tests.
drop.haq/id              : OK, passed 100 tests.
Test ran successfully.
Looks like a good patch.
Finished recording patch 'Add testsuite'

Excellent: now, patches must pass the test suite before they can be committed.

2.11 Tag the stable version, create a tarball, and sell it!

Tag the stable version:

$ darcs tag
What is the version name? 0.0
Finished tagging patch 'TAG 0.0'

2.11.1 Create a tarball

You can do this using either Cabal or darcs, or even an explicit tar command. Using Cabal

Since the code is cabalised, we can create a tarball with cabal-install directly (you can also use runhaskell Setup.hs sdist, but you need tar on your system [1]):

$ cabal sdist
Building source dist for haq-0.0...
Source tarball created: dist/haq-0.0.tar.gz

This has the advantage that Cabal will do a bit more checking, and ensure that the tarball has the structure that HackageDB expects. Note that it does require the LICENSE file to exist. It packages up the files needed to build the project; to include other files (such as Test.hs in the above example, and our README), we need to add:

extra-source-files: Tests.hs README

to the .cabal file to have everything included. Using darcs

Alternatively, you can use darcs:

$ darcs dist -d haq-0.0
Created dist as haq-0.0.tar.gz

And you're all set up!

2.11.2 Check that your source package is complete

Just to make sure everything works, try building the source package in some temporary directory:

$ tar xzf haq-0.0.tar.gz
$ cd haq-0.0
$ cabal configure
$ cabal build

and for packages containing libraries,

$ cabal haddock

2.11.3 Upload your package to Hackage

Whichever of the above methods you've used to create your package, you can upload it to the Hackage package collection via a web interface. You may wish to use the package checking interface there first, and fix things it warns about, before uploading your package.

2.12 Summary

The following files were created:

   $ ls
   Haq.hs           Tests.hs         dist             haq.cabal
   Setup.hs         _darcs           haq-0.0.tar.gz

3 Libraries


3.1 Hierarchical source


   $ mkdir Data
   $ cat > Data/LTree.hs 
   module Data.LTree where

So our Data.LTree module lives in Data/LTree.hs

3.2 The Cabal file


   $ cat > ltree.cabal 
   Name:                ltree
   Version:             0.1
   Description:         Lambda tree implementation
   License:             BSD3
   License-file:        LICENSE
   Author:              Don Stewart
   Build-Type:          Simple
   Cabal-Version:       >=1.2
     Build-Depends:     base >= 3 && < 5
     Exposed-modules:   Data.LTree
     ghc-options:       -Wall

We can thus build our library:

   $ cabal configure --prefix=$HOME --user
   $ cabal build    
   Preprocessing library ltree-0.1...
   Building ltree-0.1...


   $ cabal install
   Registering ltree-0.1...
   Saving old package config file... done.
   Writing new package config file... done.

And we're done! To try it out, first make sure that your working directory is anything but the source directory of your library:

   $ cd ..

And then use your new library from, for example, ghci:

   $ ghci -package ltree
   Prelude> :m + Data.LTree
   Prelude Data.LTree> 

The new library is in scope, and ready to go.

3.3 More complex build systems

For larger projects, you may want to store source trees in subdirectories. This can be done simply by creating a directory -- for example, "src" -- into which you will put your src tree.


   hs-source-dirs: src

You can also set up Cabal to run configure scripts, among other features. For more information consult the Cabal documentation.

4 Automation

A tool to automatically populate a new cabal project is available:

   cabal init

Usage is:

$ cabal init
Package name [default "haq"]? 
Package version [default "0.1"]? 
Please choose a license:
   1) GPL
   2) GPL-2
   3) GPL-3
   4) LGPL
   5) LGPL-2.1
   6) LGPL-3
 * 7) BSD3
   8) BSD4
   9) MIT
  10) PublicDomain
  11) AllRightsReserved
  12) OtherLicense
  13) Other (specify)
Your choice [default "BSD3"]? 
Author name? Henry Laxen
Maintainer email?
Project homepage/repo URL?
Project synopsis? A wonderful little module
Project category:
   1) Codec
   2) Concurrency
   3) Control
   4) Data
   5) Database
   6) Development
   7) Distribution
   8) Game
   9) Graphics
  10) Language
  11) Math
  12) Network
  13) Sound
  14) System
  15) Testing
  16) Text
  17) Web
  18) Other (specify)
Your choice? 3
What does the package build:
   1) Library
   2) Executable
Your choice? 1
Generating LICENSE...
Generating Setup.hs...
Generating haq.cabal...
You may want to edit the .cabal file and add a Description field.

5 Licenses

Code for the common base library package must be BSD licensed. Otherwise, it is entirely up to you as the author. Choose a licence (inspired by this). Check the licences of things you use (both other Haskell packages and C libraries), since these may impose conditions you must follow. Use the same licence as related projects, where possible. The Haskell community is split into 2 camps, roughly: those who release everything under BSD, and (L)GPLers. Some Haskellers recommend avoiding LGPL, due to cross-module optimisation issues. Like many licensing questions, this advice is controversial. Several Haskell projects (wxHaskell, HaXml, etc) use the LGPL with an extra permissive clause which gets round the cross-module optimisation problem.

6 Releases


  • darcs dist generates tarballs directly from a darcs repository

For example:

$ cd fps
$ ls       
Data      LICENSE   README    Setup.hs  TODO      _darcs    cbits dist      fps.cabal tests
$ darcs dist -d fps-0.8
Created dist as fps-0.8.tar.gz

You can now just post your fps-0.8.tar.gz


put the following in _darcs/prefs/defaults:

 apply posthook darcs dist
 apply run-posthook


  • Tag each release using darcs tag. For example:
$ darcs tag 0.8
Finished tagging patch 'TAG 0.8'


7 Hosting

Hosting for repos is available from the Haskell community server:


8 Web page


9 The user experience

When developing a new Haskell library, it is important to remember how the user expects to be able to build and use a library.

9.1 Introductory information and build guide

A typical library user expects to:

  1. Visit
  2. Find the library/program they are looking for:
    1. if not found, try mailing list;
  3. Download
  4. Build and install
  5. Enjoy

Each of these steps can pose potential road blocks, and code authors can do a lot to help code users avoid such blocks. Steps 1..2 may be easy enough, and many coders and users are mainly concerned with step 5. Steps 3..4 are the ones that often get in the way. In particular, the following questions should have clear answers:

  • Which is the latest version?
  • What state is it in?
  • What are its aims?
  • Where is the documentation?
  • How do we handle dependencies?

The best place to answer these questions is a README file, distributed with the library or application, and often accompanied with similar text on a more extensive web page.

9.2 Tutorials

Generated haddock documentation is usually not enough to help new programmers learn how to use a library. You must also provide accompanying examples, and even tutorials about the library.

Please consider providing example code for your library or application. The code should be type-correct and well-commented.

10 Program structure


11 Publicity


11.1 Join the community


11.2 Announce your project on haskell@


11.3 Add your code to the public collections

  • If your release is a Cabal package, add it to the Hackage database (Haskell's CPAN wanna-be).

11.4 Blog about it


12 Example

A complete example of writing, packaging and releasing a new Haskell library under this process has been documented.