Difference between revisions of "How to write a Haskell program"

From HaskellWiki
Jump to: navigation, search
(add example of how to use "darcs tag")
m (Added more <nowiki> tags)
 
(89 intermediate revisions by 40 users not shown)
Line 1: Line 1:
A guide to the best practice for creating a new Haskell project or
+
A developers' guide to creating a new Haskell project or program, and working in the Haskell developer ecosystem.
program.
+
  +
''Note: for learning the Haskell language itself we recommend [http://haskell.org/haskellwiki/Tutorials#Introductions_to_Haskell these resources].''
   
 
== Recommended tools ==
 
== Recommended tools ==
   
Almost all new Haskell projects use the following tools.
+
Almost all new Haskell projects use the following tools. Each is
Each is intrinsically useful, but using a set of common tools also benefits everyone by increasing productivity, and you're more likely to get patches.
+
intrinsically useful, but using a set of common tools also helps
  +
everyone by increasing productivity, and you're more likely to get
  +
patches.
   
 
=== Revision control ===
 
=== Revision control ===
   
Use [http://darcs.net Darcs] unless you have a specific reason not to.
 
  +
Use [http://git-scm.com/ git] or [http://darcs.net darcs] unless you have a specific reason not to. Both are lightweight distributed revision control system. Darcs is written in Haskell. They are the two most popular [http://en.wikipedia.org/wiki/Distributed_version_control_system DVCS]es in the Haskell world. If you want to encourage contributions from other Haskell hackers then git is best. For git, [http://github.com/ GitHub] is very popular. Darcs hosting is available on [http://hub.darcs.net/ hub.darcs.net].
It's much more powerful than most competing systems (and it's written in Haskell).
 
   
 
=== Build system ===
 
=== Build system ===
   
Use [http://haskell.org/cabal Cabal].
 
  +
[[Image:Cabal-With-Text-small.png|frame|Built with Cabal]]
You should read at least the start of section 2 of the [http://www.haskell.org/ghc/docs/latest/html/Cabal/index.html Cabal User's Guide].
 
  +
  +
Use [http://haskell.org/cabal/ Cabal].
  +
You should read at least the start of section 2 of the [http://www.haskell.org/cabal/users-guide/ Cabal User's Guide].
  +
  +
You should use [http://haskell.org/cabal/download.html cabal-install] as a front-end for installing 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 a [http://www.haskell.org/cabal/download.html binary distribution] or as part of the [http://haskell.org/platform Haskell Platform], so you can probably assume your users will have it too.
   
 
=== Documentation ===
 
=== Documentation ===
   
For libraries, use [http://haskell.org/haddock Haddock]. We recommend
+
For libraries, use [http://haskell.org/haddock/ Haddock]. Haddock generates [http://hackage.haskell.org/package/base-4.7.0.0/docs/Prelude.html nice markup], with links to source.
using the latest version of haddock (currently 0.8).
 
   
 
=== Testing ===
 
=== Testing ===
   
Pure code can be tested using [http://www.md.chalmers.se/~rjmh/QuickCheck/ QuickCheck] or [http://www.mail-archive.com/haskell@haskell.org/msg19215.html SmallCheck], impure code with [http://hunit.sourceforge.net/ HUnit].
 
  +
Typical unit/spec based testing, particularly with impure code, can be done with [http://hspec.github.io/ HSpec] and [http://hackage.haskell.org/package/HUnit HUnit].
  +
  +
You can use [http://hackage.haskell.org/package/QuickCheck QuickCheck] or [http://www.mail-archive.com/haskell@haskell.org/msg19215.html SmallCheck] to test pure code. These libraries work best when you have known invariants in your code's behavior. See [http://hackage.haskell.org/package/hashable-1.1.2.2/hashable.cabal this Cabal file] for an example of how to include tests in your Cabal package.
  +
  +
To get started, try [[Introduction to QuickCheck]]. For a slightly more advanced introduction, [http://blog.codersbase.com/posts/2006-09-01-simple-unit-testing.html 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]]
  +
  +
[https://github.com/sol/doctest#readme doctest] is another testing method similar to python [https://en.wikipedia.org/wiki/Doctest Doctest]
  +
  +
=== Distribution ===
  +
  +
The standard mechanism for distributing Haskell libraries and
  +
applications is [http://hackage.haskell.org/packages/hackage.html Hackage]. Hackage can
  +
host your cabalised tarball releases, and link to any library
  +
dependencies your code has. Users will find and install your packages via "cabal install", and your package will be integrated into Haskell search engines, like [http://www.haskell.org/hoogle/ hoogle]
  +
  +
=== Target Environment ===
   
To get started, try [[Introduction to QuickCheck]]. For a slightly more advanced introduction, [http://blog.codersbase.com/2006/09/01/simple-unit-testing-in-haskell/ Simple Unit Testing in Haskell] is a blog article about creating a testing framework for QuickCheck using some Template Haskell.
 
  +
If possible, depend on libraries that are provided by the current stable [http://daniel-diaz.github.io/stackagelist/ Stackage package versions] or the [http://haskell.org/platform Haskell Platform]. Ideally your package should be able to build with users that keep current with Stackage or users of the Platform.
   
 
== Structure of a simple project ==
 
== Structure of a simple project ==
   
 
The basic structure of a new Haskell project can be adopted from
 
The basic structure of a new Haskell project can be adopted from
[http://semantic.org/hnop/ HNop], the minimal Haskell project. It
+
[https://github.com/strangemonad/hnop HNop], the minimal Haskell project. It
 
consists of the following files, for the mythical project "haq".
 
consists of the following files, for the mythical project "haq".
   
Line 36: Line 56:
 
* haq.cabal -- the cabal build description
 
* haq.cabal -- the cabal build description
 
* Setup.hs -- build script itself
 
* Setup.hs -- build script itself
* _darcs -- revision control
+
* .git -- revision control
 
* README -- info
 
* README -- info
 
* LICENSE -- license
 
* LICENSE -- license
   
You can of course elaborate on this, with subdirectories and multiple
+
Of course, you can elaborate on this, with subdirectories and multiple
modules.
+
modules. See [[Structure of a Haskell project]] for an example of a larger project's directory structure.
   
Here is a transcript on how you'd create a minimal darcs and cabalised
+
Here is a transcript that shows how you'd create a minimal git and cabalised
Haskell project, for the cool new Haskell program "haq", build it,
+
Haskell project for the cool new Haskell program "haq", build it,
 
install it and release.
 
install it and release.
   
The new tool 'mkcabal' automates all this for you, but its important to
+
''Note'': The new tool "cabal init" automates all this for you, but you should
understand all the parts first.
+
understand all the parts even so.
   
 
We will now walk through the creation of the infrastructure for a simple
 
We will now walk through the creation of the infrastructure for a simple
Line 57: Line 77:
 
Create somewhere for the source:
 
Create somewhere for the source:
   
<haskell>
 
  +
<code>
$ mkdir haq
+
$ mkdir haq
$ cd haq
+
$ cd haq
</haskell>
+
</code>
   
 
=== Write some Haskell source ===
 
=== Write some Haskell source ===
Line 69: Line 89:
 
$ cat > Haq.hs
 
$ cat > Haq.hs
 
--
 
--
-- Copyright (c) 2006 Don Stewart - http://www.cse.unsw.edu.au/~dons
+
-- Copyright (c) 2006 Don Stewart - http://www.cse.unsw.edu.au/~dons/
 
-- GPL version 2 or later (see http://www.gnu.org/copyleft/gpl.html)
 
-- GPL version 2 or later (see http://www.gnu.org/copyleft/gpl.html)
 
--
 
--
Line 81: Line 101:
 
</haskell>
 
</haskell>
   
=== Stick it in darcs ===
+
=== Stick it in version control ===
   
Place the source under revision control:
+
Place the source under revision control (you may need to enter your e-mail address first, to identify you as maintainer of this source):
   
<haskell>
 
  +
<code>
$ darcs init
+
$ git init
$ darcs add Haq.hs
+
$ git add Haq.hs
$ darcs record
+
$ git commit -am "my commit message"
addfile ./Haq.hs
+
</code>
Shall I record this change? (1/?) [ynWsfqadjkc], or ? for help: y
 
hunk ./Haq.hs 1
 
+--
 
+-- Copyright (c) 2006 Don Stewart - http://www.cse.unsw.edu.au/~dons
 
+-- GPL version 2 or later (see http://www.gnu.org/copyleft/gpl.html)
 
+--
 
+import System.Environment
 
+
 
+-- | 'main' runs the main program
 
+main :: IO ()
 
+main = getArgs >>= print . haqify . head
 
+
 
+haqify s = "Haq! " ++ s
 
Shall I record this change? (2/?) [ynWsfqadjkc], or ? for help: y
 
What is the patch name? Import haq source
 
Do you want to add a long comment? [yn]n
 
Finished recording patch 'Import haq source'
 
</haskell>
 
 
And we can see that darcs is now running the show:
 
 
<haskell>
 
$ ls
 
Haq.hs _darcs
 
</haskell>
 
   
 
=== Add a build system ===
 
=== Add a build system ===
   
 
Create a .cabal file describing how to build your project:
 
Create a .cabal file describing how to build your project:
  +
<code>
  +
$ cabal init
  +
</code>
   
<haskell>
 
  +
that will ask a few questions about your project and generate a file similar to the example:
$ cat > haq.cabal
 
Name: haq
 
Version: 0.0
 
Description: Super cool mega lambdas
 
License: GPL
 
License-file: LICENSE
 
Author: Don Stewart
 
Maintainer: dons@cse.unsw.edu.au
 
Build-Depends: base
 
   
Executable: haq
 
  +
<code>
Main-is: Haq.hs
 
  +
-- Initial scratch.cabal generated by cabal init. For further
ghc-options: -O
 
  +
-- documentation, see http://haskell.org/cabal/users-guide/
</haskell>
 
  +
  +
name: haq
  +
version: 0.1.0.0
  +
description: Super cool mega lambdas
  +
license: GPL
  +
license-file: LICENSE
  +
author: Don Stewart
  +
maintainer: dons@cse.unsw.edu.au
  +
build-type: Simple
  +
cabal-version: >=1.10
  +
  +
executable haq
  +
main-is: Haq.hs
  +
build-depends: base >=4.5 && <4.8
  +
default-language: Haskell2010
  +
</code>
   
(If your package uses other packages, e.g. <tt>haskell98</tt>, you'll need to add them to the <tt>Build-Depends:</tt> field.)
+
(If your package uses other packages, e.g. <tt>text</tt>, you'll need to add them to the <tt>build-depends:</tt> field as a comma separated list.)
Add a <tt>Setup.lhs</tt> that will actually do the building:
 
   
<haskell>
 
  +
Cabal will also generate a <tt>Setup.hs</tt> file that will do the actual building. You will rarely need to modify it.
$ cat > Setup.lhs
 
#! /usr/bin/env runhaskell
 
   
> import Distribution.Simple
 
  +
If you specifed a known license, it will also add a LICENSE file.
> main = defaultMain
 
  +
</haskell>
 
  +
You might like to add a README file to tell what your project is about.
Cabal allows either <tt>Setup.hs</tt> or <tt>Setup.lhs</tt>, but we recommend writing the setup file this way so that it can be executed directly by Unix shells.
 
   
 
Record your changes:
 
Record your changes:
   
<haskell>
 
  +
<code>
$ darcs add haq.cabal Setup.lhs
+
$ git add haq.cabal Setup.hs LICENSE README
$ darcs record --all
+
$ git commit -am "Add a build system"
What is the patch name? Add a build system
+
</code>
Do you want to add a long comment? [yn]n
 
Finished recording patch 'Add a build system'
 
</haskell>
 
   
 
=== Build your project ===
 
=== Build your project ===
   
Now build it!
 
  +
Now build it! There are two methods of accessing Cabal functionality: through your Setup.hs script or through cabal-install. cabal-install is now the preferred method.
   
<haskell>
 
  +
Building using cabal-install and sandboxes, always use sandboxes in your projects unless you are an expert and know what you are doing! Demonstrated here:
$ runhaskell Setup.lhs configure --prefix=$HOME
 
  +
$ runhaskell Setup.lhs build
 
  +
<code>
$ runhaskell Setup.lhs install
 
  +
$ cabal sandbox init
</haskell>
 
  +
$ cabal install -j
  +
</code>
  +
  +
This will install your newly minted haq program in $PROJECT_DIR/.cabal-sandbox/bin.
   
 
=== Run it ===
 
=== Run it ===
   
 
And now you can run your cool project:
 
And now you can run your cool project:
<haskell>
 
  +
<code>
$ haq me
+
$ .cabal-sandbox/bin/haq me
"Haq! me"
+
"Haq! me"
</haskell>
+
</code>
   
You can also run it in-place, avoiding the install phase:
 
  +
Since our program doesn't rely on any other assets we can just copy it to a directory in our path if we want to be able to use it from anywhere without referencing the sandbox directory. Like so:
<haskell>
 
  +
$ dist/build/haq/haq you
 
  +
<code>
"Haq! you"
 
  +
$ sudo cp .cabal-sandbox/bin/haq /usr/local/bin/haq
</haskell>
 
  +
$ haq me
  +
"Haq! me"
  +
</code>
   
 
=== Build some haddock documentation ===
 
=== Build some haddock documentation ===
Line 157: Line 188:
 
Generate some API documentation into dist/doc/*
 
Generate some API documentation into dist/doc/*
   
<haskell>
 
  +
Using cabal install:
$ runhaskell Setup.lhs haddock
 
  +
<code>
</haskell>
 
  +
$ cabal haddock
  +
</code>
  +
  +
Using cabal install if you planned to upload your haq package to Hackage:
  +
<br><code><nowiki>
  +
$ cabal haddock --hyperlink-source --html-location='http://hackage.haskell.org/package/haq/docs' --contents-location='http://hackage.haskell.org/package/haq'
  +
</nowiki></code>
   
 
which generates files in dist/doc/ including:
 
which generates files in dist/doc/ including:
   
<haskell>
 
  +
<code>
$ w3m -dump dist/doc/html/haq/Main.html
+
$ w3m -dump dist/doc/html/haq/Main.html
 
haq Contents Index
 
haq Contents Index
 
Main
 
Main
 
  +
 
Synopsis
 
Synopsis
 
main :: IO ()
 
main :: IO ()
 
  +
 
Documentation
 
Documentation
 
  +
 
main :: IO ()
 
main :: IO ()
 
main runs the main program
 
main runs the main program
 
  +
 
Produced by Haddock version 0.7
 
Produced by Haddock version 0.7
</haskell>
+
</code>
  +
  +
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.
   
=== Add some automated testing: QuickCheck ===
 
  +
=== (Optional) Improve your code: HLint ===
   
We'll use QuickCheck to specify a simple property of our Haq.hs code. Create a tests module, Tests.hs, with some QuickCheck boilerplate:
 
  +
[http://hackage.haskell.org/package/hlint HLint] can be a valuable tool for improving your coding style, particularly if you're new to Haskell. Let's run it now.
   
<haskell>
 
  +
<code>
$ cat > Tests.hs
 
  +
$ hlint .
import Char
 
  +
./Haq.hs:11:1: Warning: Eta reduce
import List
 
  +
Found:
import Test.QuickCheck
 
  +
haqify s = "Haq! " ++ s
import Text.Printf
 
  +
Why not:
  +
haqify = ("Haq! " ++)
  +
</code>
   
main = mapM_ (\(s,a) -> printf "%-25s: " s >> a) tests
 
  +
The existing code will work, but let's follow that suggestion. Open Haq.hs in your favourite editor and change the line:
   
instance Arbitrary Char where
 
  +
<haskell>
arbitrary = choose ('\0', '\128')
 
  +
where haqify s = "Haq! " ++ s
coarbitrary c = variant (ord c `rem` 4)
 
 
</haskell>
 
</haskell>
   
Now let's write a simple property:
 
  +
to:
   
 
<haskell>
 
<haskell>
$ cat >> Tests.hs
 
  +
where haqify = ("Haq! " ++)
-- reversing twice a finite list, is the same as identity
 
prop_reversereverse s = (reverse . reverse) s == id s
 
where _ = s :: [Int]
 
 
-- and add this to the tests list
 
tests = [("reverse.reverse/id", test prop_reversereverse)]
 
 
</haskell>
 
</haskell>
   
We can now run this test, and have QuickCheck generate the test data:
 
  +
=== Add some tests ===
   
<haskell>
 
  +
==== HSpec ====
$ runhaskell Tests.hs
 
reverse.reverse/id : OK, passed 100 tests.
 
</haskell>
 
   
Let's add a test for the 'haqify' function:
 
  +
If you'd like to use HSpec:
   
<haskell>
 
  +
To your cabal, add:
-- Dropping the "Haq! " string is the same as identity
 
prop_haq s = drop (length "Haq! ") (haqify s) == id s
 
where haqify s = "Haq! " ++ s
 
   
tests = [("reverse.reverse/id", test prop_reversereverse)
 
  +
<code>
,("drop.haq/id", test prop_haq)]
 
  +
test-suite tests
</haskell>
 
  +
ghc-options: -Wall
  +
default-extensions: OverloadedStrings
  +
type: exitcode-stdio-1.0
  +
main-is: HSpecTests.hs
  +
build-depends: base,
  +
haq,
  +
hspec >= 1.8
  +
default-language: Haskell2010
  +
</code>
   
and let's test that:
 
  +
To enable tests and install HSpec (and any other needed dependencies):
  +
  +
<code>
  +
$ cabal install --enable-tests
  +
</code>
  +
  +
Then for your actual file containing the test code:
   
 
<haskell>
 
<haskell>
$ runhaskell Tests.hs
 
  +
$ cat > HSpecTests.hs
reverse.reverse/id : OK, passed 100 tests.
 
  +
module Main where
drop.haq/id : OK, passed 100 tests.
 
  +
  +
import Haq
  +
import Test.Hspec
  +
  +
main :: IO ()
  +
main = hspec $ do
  +
  +
describe "Validate haqify function" $ do
  +
it "haqify is supposed to prefix Haq! to things" $ do
  +
haqify "me" `shouldBe` "Haq! me"
  +
 
</haskell>
 
</haskell>
   
Great!
 
  +
Execute the tests with:
   
=== Running the test suite from darcs ===
 
  +
<code>
  +
$ cabal test
  +
</code>
   
We can arrange for darcs to run the test suite on every commit:
 
  +
==== QuickCheck ====
  +
  +
If you're using QuickCheck:
   
 
<haskell>
 
<haskell>
$ darcs setpref test "runhaskell Tests.hs"
 
  +
$ cat > QuickTests.hs
Changing value of test from '' to 'runhaskell Tests.hs'
 
</haskell>
 
   
will run the full set of QuickChecks. (If your test requires it you may need to ensure other things are built too eg: <tt>darcs setpref test "alex Tokens.x;happy Grammar.y;runhaskell Tests.hs"</tt>).
 
  +
import Data.Char
  +
import Data.List
  +
import Haq
  +
import Test.QuickCheck
  +
import Text.Printf
   
Let's commit a new patch:
 
  +
main = mapM_ (\(s,a) -> printf "%-25s: " s >> a) tests
   
<haskell>
 
  +
-- reversing twice a finite list, is the same as identity
$ darcs add Tests.hs
 
  +
prop_reversereverse s = (reverse . reverse) s == id s
$ darcs record --all
 
  +
where _ = s :: [Int]
What is the patch name? Add testsuite
 
  +
Do you want to add a long comment? [yn]n
 
  +
-- Dropping the "Haq! " string is the same as identity
Running test...
 
  +
prop_haq s = drop (length "Haq! ") (haqify s) == id s
reverse.reverse/id : OK, passed 100 tests.
 
  +
where haqify s = "Haq! " ++ s
drop.haq/id : OK, passed 100 tests.
 
  +
Test ran successfully.
 
  +
tests = [("reverse.reverse/id", quickCheck prop_reversereverse)
Looks like a good patch.
 
  +
,("drop.haq/id", quickCheck prop_haq)]
Finished recording patch 'Add testsuite'
 
 
</haskell>
 
</haskell>
   
Excellent, now patches must pass the test suite before they can be
 
  +
To run the test:
committed.
 
  +
  +
<code>
  +
$ runhaskell Tests.hs
  +
reverse.reverse/id : +++ OK, passed 100 tests.
  +
drop.haq/id : +++ OK, passed 100 tests.
  +
</code>
  +
  +
Success!
   
 
=== Tag the stable version, create a tarball, and sell it! ===
 
=== Tag the stable version, create a tarball, and sell it! ===
Line 270: Line 332:
 
Tag the stable version:
 
Tag the stable version:
   
<haskell>
 
  +
<code>
$ darcs tag
+
$ git tag 0.0
What is the version name? 0.0
+
</code>
Finished tagging patch 'TAG 0.0'
 
</haskell>
 
   
==== Tarballs via Cabal ====
+
==== Create a tarball ====
  +
You can do this using either Cabal or an explicit <tt>tar</tt> command.
   
Since the code is cabalised, we can create a tarball with Cabal
 
  +
===== Using Cabal =====
directly:
 
   
<haskell>
 
  +
Since the code is cabalised, we can create a tarball with cabal-install
$ runhaskell Setup.lhs sdist
 
  +
directly (you can also use <tt>runhaskell Setup.hs sdist</tt>, but you need <tt>tar</tt> on your system [http://thread.gmane.org/gmane.comp.lang.haskell.cafe/60617/focus=60653]):
Building source dist for haq-0.0...
 
  +
Source tarball created: dist/haq-0.0.tar.gz
 
  +
<code>
</haskell>
 
  +
$ cabal sdist
  +
Building source dist for haq-0.0...
  +
Source tarball created: dist/haq-0.0.tar.gz
  +
</code>
 
This has the advantage that Cabal will do a bit more checking, and
 
This has the advantage that Cabal will do a bit more checking, and
ensure that the tarball has the structure expected by HackageDB.
+
ensure that the tarball has the structure that HackageDB expects.
It packages up the files needed to build the project; to include other files (such as <tt>Test.hs</tt> in the above example), we need to add:
+
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 <tt>Test.hs</tt> in the above example, and our README), we need to add:
   
<haskell>
 
  +
<code>
extra-source-files: Tests.hs
+
extra-source-files: Tests.hs README
</haskell>
+
</code>
   
 
to the .cabal file to have everything included.
 
to the .cabal file to have everything included.
   
==== Tarballs via darcs ====
 
  +
==== Check that your source package is complete ====
   
Alternatively, you can use darcs:
 
  +
Just to make sure everything works, try building the source package in some temporary directory:
<haskell>
 
  +
<code>
$ darcs dist -d haq-0.0
+
$ tar xzf haq-0.0.tar.gz
Created dist as haq-0.0.tar.gz
+
$ cd haq-0.0
</haskell>
+
$ cabal configure
  +
$ cabal build
  +
</code>
  +
and for packages containing libraries,
  +
<code>
  +
$ cabal haddock
  +
</code>
  +
  +
==== 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 [http://hackage.haskell.org/upload web interface].
  +
You may wish to use the package checking interface there first, and fix things it warns about, before uploading your package.
  +
  +
==== Upload your package documentation to Hackage ====
  +
  +
Assuming you built your documentation like this:
  +
  +
<code><nowiki>
  +
$ cabal haddock --hyperlink-source --html-location='http://hackage.haskell.org/package/haq/docs' --contents-location='http://hackage.haskell.org/package/haq'
  +
</nowiki></code>
  +
  +
You can force upload (so you don't have to wait on the server to build it for you) your documentation for your package like so:
   
And you're all set up!
 
  +
<code><nowiki>
  +
$ cp -R ./dist/doc/html/haq/ haq-0.0-docs
  +
$ tar cvzf --format=ustar -f haq-0.0-docs.tar.gz haq-0.0-docs
  +
$ curl -X PUT -H 'Content-Type: application/x-tar' -H 'Content-Encoding: gzip' --data-binary '@haq-0.0-docs.tar.gz' 'https://$USERNAME:$PASSWORD@hackage.haskell.org/package/haq-0.0/docs'
  +
</nowiki></code>
   
 
=== Summary ===
 
=== Summary ===
Line 309: Line 377:
   
 
$ ls
 
$ ls
Haq.hs Tests.hs dist haq.cabal
+
Haq.hs HSpecTests.hs dist haq.cabal
Setup.lhs _darcs haq-0.0.tar.gz
+
Setup.hs .git haq-0.0.tar.gz QuickTests.hs
   
 
== Libraries ==
 
== Libraries ==
Line 320: Line 388:
   
 
The source should live under a directory path that fits into the
 
The source should live under a directory path that fits into the
existing [http://www.haskell.org/~simonmar/lib-hierarchy.html module layout guide].
+
existing [[Hierarchical module names|module layout guide]].
 
So we would create the following directory structure, for the module
 
So we would create the following directory structure, for the module
 
Data.LTree:
 
Data.LTree:
Line 335: Line 403:
 
no executable section:
 
no executable section:
   
$ cat ltree.cabal
+
$ cat > ltree.cabal
 
Name: ltree
 
Name: ltree
 
Version: 0.1
 
Version: 0.1
Line 343: Line 411:
 
Author: Don Stewart
 
Author: Don Stewart
 
Maintainer: dons@cse.unsw.edu.au
 
Maintainer: dons@cse.unsw.edu.au
Build-Depends: base
+
Build-Type: Simple
Exposed-modules: Data.LTree
+
Cabal-Version: >=1.2
ghc-options: -Wall -O
+
  +
Library
  +
Build-Depends: base >= 3 && < 5
  +
Exposed-modules: Data.LTree
  +
ghc-options: -Wall
   
 
We can thus build our library:
 
We can thus build our library:
   
$ runhaskell Setup.lhs configure --prefix=$HOME
+
$ cabal configure --prefix=$HOME --user
$ runhaskell Setup.lhs build
+
$ cabal build
 
Preprocessing library ltree-0.1...
 
Preprocessing library ltree-0.1...
 
Building ltree-0.1...
 
Building ltree-0.1...
Line 358: Line 426:
 
and our library has been created as a object archive. Now install it:
 
and our library has been created as a object archive. Now install it:
   
$ runhaskell Setup.lhs install
+
$ cabal install
 
Installing: /home/dons/lib/ltree-0.1/ghc-6.6 & /home/dons/bin ltree-0.1...
 
Installing: /home/dons/lib/ltree-0.1/ghc-6.6 & /home/dons/bin ltree-0.1...
 
Registering ltree-0.1...
 
Registering ltree-0.1...
Line 365: Line 433:
 
Writing new package config file... done.
 
Writing new package config file... done.
   
And we're done! You can use your new library from, for example, ghci:
 
  +
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
 
$ ghci -package ltree
Line 375: Line 448:
 
=== More complex build systems ===
 
=== More complex build systems ===
   
For larger projects it is useful to have source trees stored in
 
  +
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.
subdirectories. This can be done simply by creating a directory, for
 
example, "src", into which you will put your src tree.
 
   
 
To have Cabal find this code, you add the following line to your Cabal
 
To have Cabal find this code, you add the following line to your Cabal
Line 384: Line 455:
 
hs-source-dirs: src
 
hs-source-dirs: src
   
Cabal can set up to also run configure scripts, along with a range of
+
You can also set up Cabal to run configure scripts, among other features. For more information consult the
other features. For more information consult the
+
[http://www.haskell.org/cabal/users-guide/ Cabal user guide].
[http://www.haskell.org/ghc/docs/latest/html/Cabal/index.html Cabal documentation].
 
 
== Automation ==
 
 
A tool to automatically populate a new cabal project is available
 
(beta!):
 
 
darcs get http://www.cse.unsw.edu.au/~dons/code/mkcabal
 
 
Usage is:
 
 
<haskell>
 
$ mkcabal
 
Project name: haq
 
What license ["GPL","LGPL","BSD3","BSD4","PublicDomain","AllRightsReserved"] ["BSD3"]:
 
What kind of project [Executable,Library] [Executable]:
 
Is this your name? - "Don Stewart " [Y/n]:
 
Is this your email address? - "<dons@cse.unsw.edu.au>" [Y/n]:
 
Created Setup.lhs and haq.cabal
 
$ ls
 
Haq.hs LICENSE Setup.lhs _darcs dist haq.cabal
 
</haskell>
 
 
which will fill out some stub Cabal files for the project 'haq'.
 
 
To create an entirely new project tree:
 
 
<haskell>
 
$ mkcabal --init-project
 
Project name: haq
 
What license ["GPL","LGPL","BSD3","BSD4","PublicDomain","AllRightsReserved"] ["BSD3"]:
 
What kind of project [Executable,Library] [Executable]:
 
Is this your name? - "Don Stewart " [Y/n]:
 
Is this your email address? - "<dons@cse.unsw.edu.au>" [Y/n]:
 
Created new project directory: haq
 
$ cd haq
 
$ ls
 
Haq.hs LICENSE README Setup.lhs haq.cabal
 
</haskell>
 
   
 
== Licenses ==
 
== Licenses ==
Line 391: Line 462:
 
Code for the common base library package must be BSD licensed. Otherwise, it
 
Code for the common base library package must be BSD licensed. Otherwise, it
 
is entirely up to you as the author.
 
is entirely up to you as the author.
Choose a licence (inspired by [http://www.dina.dk/~abraham/rants/license.html this]).
+
Choose a licence (inspired by [http://archive.is/http://web.archive.org/web/20031206042644/http://www.dina.dk/~abraham/rants/license.html this]).
Check the licences of things you use, both other Haskell packages and C
+
Check the licences of things you use (both other Haskell packages and C
libraries, since these may impose conditions you must follow.
+
libraries), since these may impose conditions you must follow.
 
Use the same licence as related projects, where possible. The Haskell community is
 
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
+
split into 2 camps, roughly: those who release everything under BSD, and
(L)GPLers. Some Haskellers recommend avoiding LGPL, due to cross module optimisation
+
(L)GPLers. Some Haskellers recommend avoiding LGPL, due to cross-module optimisation
 
issues. Like many licensing questions, this advice is controversial. Several Haskell projects
 
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
 
(wxHaskell, HaXml, etc) use the LGPL with an extra permissive clause which gets round the
cross-module optimisation thing.
+
cross-module optimisation problem.
   
 
== Releases ==
 
== Releases ==
   
It's important to release your code as stable, tagged tarballs. Don't
+
It's important to release your code as stable, tagged tarballs. Don't just rely on your commit hashes for tracking history, tag your released versions and milestones!
just [http://awayrepl.blogspot.com/2006/11/we-dont-do-releases.html rely on darcs for distribution].
 
   
* '''darcs dist''' generates tarballs directly from a darcs repository
+
* '''git archive''' generates tarballs directly from a git repository based on the provided tag.
   
 
For example:
 
For example:
Line 411: Line 482:
 
$ cd fps
 
$ cd fps
 
$ ls
 
$ ls
Data LICENSE README Setup.hs TODO _darcs cbits dist fps.cabal tests
+
Data LICENSE README Setup.hs TODO .git cbits dist fps.cabal tests
$ darcs dist -d fps-0.8
+
$ git tag v0.8
Created dist as fps-0.8.tar.gz
+
$ git archive --format=tar.gz v0.8 > fps-0.8.tar.gz
   
 
You can now just post your fps-0.8.tar.gz
 
You can now just post your fps-0.8.tar.gz
   
You can also have darcs do the equivalent of 'daily snapshots' for you by using a post-hook.
 
   
put the following in _darcs/prefs/defaults:
 
  +
* Tag each release using '''git tag'''. For example:
apply posthook darcs dist
 
apply run-posthook
 
 
Advice:
 
* Tag each release using '''darcs tag'''. For example:
 
 
$ darcs tag 0.8
 
Finished tagging patch 'TAG 0.8'
 
 
Then people can <tt>darcs pull --partial -t 0.8</tt>, to get just the tagged version (and not the entire history).
 
   
 
== Hosting ==
 
== Hosting ==
   
A Darcs repository can be published simply by making it available from a
 
  +
Hosting for repos is available from [http://github.com Github] and the [http://community.haskell.org/ Haskell community server]
web page. If you don't have an account online, or prefer not to do this
 
yourself, source can be hosted on darcs.haskell.org (you will need to
 
email [http://research.microsoft.com/~simonmar/ Simon Marlow] to do this).
 
haskell.org itself has some user accounts available.
 
   
There are also many free hosting places for open source, such as
 
  +
There is also a (minimal) Github equivalent for Darcs at [http://hub.darcs.net/ hub.darcs.net].
* [http://code.google.com/hosting/ Google Project Hosting]
 
* [http://sourceforge.net/ SourceForge].
 
   
 
== Web page ==
 
== Web page ==
   
 
Create a web page documenting your project! An easy way to do this is to
 
Create a web page documenting your project! An easy way to do this is to
add a project specific page to [[Haskell|the Haskell wiki]]
+
add a project specific page to [[HaskellWiki:Contributing|the Haskell wiki]]
  +
  +
== 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.
  +
  +
=== Introductory information and build guide ===
  +
  +
A typical library user expects to:
  +
  +
# Visit [[Haskell|Haskell.org]]
  +
# Find the library/program they are looking for:
  +
## if not found, try mailing list;
  +
## if it is hidden, try improving the documentation on haskell.org;
  +
## if it does not exist, try contributing code and documentation)
  +
# Download
  +
# Build and install
  +
# 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?
  +
* Which is the right version for given OS and Haskell implementation?
  +
* How is it packaged, and what tools are needed to get and unpack it?
  +
* How is it installed, and what tools are needed to install it?
  +
* How do we handle dependencies?
  +
* How do we provide/acquire the knowledge and tool-chains needed?
  +
  +
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.
  +
  +
=== 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.
   
 
== Program structure ==
 
== Program structure ==
   
 
Monad transformers are very useful for programming in the large,
 
Monad transformers are very useful for programming in the large,
encapsulating state, and controlling side effects. To learn more about this approach, try [http://uebb.cs.tu-berlin.de/~magr/pub/Transformers.en.html Monad Transformers Step by Step].
+
encapsulating state, and controlling side effects. To learn more about this approach, try [http://www.grabmueller.de/martin/www/pub/Transformers.en.html Monad Transformers Step by Step].
   
 
== Publicity ==
 
== Publicity ==
Line 466: Line 520:
 
Most important: announce your project releases to the haskell@haskell.org mailing list. Tag your email subject line with "ANNOUNCE: ...". This ensure it will then make it into the [http://haskell.org/haskellwiki/HWN Haskell Weekly News]. To be doubly sure, you can email the release text to the [[HWN|HWN editor]].
 
Most important: announce your project releases to the haskell@haskell.org mailing list. Tag your email subject line with "ANNOUNCE: ...". This ensure it will then make it into the [http://haskell.org/haskellwiki/HWN Haskell Weekly News]. To be doubly sure, you can email the release text to the [[HWN|HWN editor]].
   
=== Add your code to the libraries page ===
+
=== Add your code to the public collections ===
  +
  +
* Add your library or application to the [[Libraries and tools]] page, under the relevant category, so people can find it.
  +
  +
* If your release is a Cabal package, add it to the [http://hackage.haskell.org/packages/hackage.html Hackage database]
   
Add your library or application to the [[Libraries and tools]] page,
 
  +
* If your library stays up to date with its dependencies, add it to the vetted packages on [https://github.com/fpco/stackage Stackage].
under the relevant category, so people can find it. Soon this will also
 
mean adding your code to the [http://hackage.homedns.org/view/ hackagedb] (Haskell's CPAN).
 
   
 
=== Blog about it ===
 
=== Blog about it ===

Latest revision as of 21:21, 26 August 2018

A developers' guide to creating a new Haskell project or program, and working in the Haskell developer ecosystem.

Note: for learning the Haskell language itself we recommend these resources.

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.

Revision control

Use git or darcs unless you have a specific reason not to. Both are lightweight distributed revision control system. Darcs is written in Haskell. They are the two most popular DVCSes in the Haskell world. If you want to encourage contributions from other Haskell hackers then git is best. For git, GitHub is very popular. Darcs hosting is available on hub.darcs.net.

Build system

Built with Cabal

Use Cabal. You should read at least the start of section 2 of the Cabal User's Guide.

You should use cabal-install as a front-end for installing 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 a binary distribution or as part of the Haskell Platform, so you can probably assume your users will have it too.

Documentation

For libraries, use Haddock. Haddock generates nice markup, with links to source.

Testing

Typical unit/spec based testing, particularly with impure code, can be done with HSpec and HUnit.

You can use QuickCheck or SmallCheck to test pure code. These libraries work best when you have known invariants in your code's behavior. See this Cabal file for an example of how to include tests in your Cabal package.

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

doctest is another testing method similar to python Doctest

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. Users will find and install your packages via "cabal install", and your package will be integrated into Haskell search engines, like hoogle

Target Environment

If possible, depend on libraries that are provided by the current stable Stackage package versions or the Haskell Platform. Ideally your package should be able to build with users that keep current with Stackage or users of the Platform.

Structure of a simple project

The basic structure of a new Haskell project can be adopted from HNop, the minimal Haskell project. It consists of the following files, for the mythical project "haq".

  • Haq.hs -- the main haskell source file
  • haq.cabal -- the cabal build description
  • Setup.hs -- build script itself
  • .git -- 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 git and cabalised Haskell project for the cool new Haskell program "haq", build it, install it and release.

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

We will now walk through the creation of the infrastructure for a simple Haskell executable. Advice for libraries follows after.

Create a directory

Create somewhere for the source:

$ mkdir haq
$ cd haq

Write some Haskell source

Write your program:

$ cat > Haq.hs
--
-- Copyright (c) 2006 Don Stewart - http://www.cse.unsw.edu.au/~dons/
-- GPL version 2 or later (see http://www.gnu.org/copyleft/gpl.html)
--
import System.Environment

-- | 'main' runs the main program
main :: IO ()
main = getArgs >>= print . haqify . head

haqify s = "Haq! " ++ s

Stick it in version control

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

$ git init
$ git add Haq.hs 
$ git commit -am "my commit message"

Add a build system

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

$ cabal init

that will ask a few questions about your project and generate a file similar to the example:

 -- Initial scratch.cabal generated by cabal init.  For further 
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                haq
 version:             0.1.0.0
 description:         Super cool mega lambdas
 license:             GPL
 license-file:        LICENSE
 author:              Don Stewart
 maintainer:          dons@cse.unsw.edu.au
 build-type:          Simple
 cabal-version:       >=1.10
 
 executable haq
   main-is:           Haq.hs 
   build-depends:     base >=4.5 && <4.8
   default-language:  Haskell2010

(If your package uses other packages, e.g. text, you'll need to add them to the build-depends: field as a comma separated list.)

Cabal will also generate a Setup.hs file that will do the actual building. You will rarely need to modify it.

If you specifed a known license, it will also add a LICENSE file.

You might like to add a README file to tell what your project is about.

Record your changes:

$ git add haq.cabal Setup.hs LICENSE README
$ git commit -am "Add a build system"

Build your project

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

Building using cabal-install and sandboxes, always use sandboxes in your projects unless you are an expert and know what you are doing! Demonstrated here:

$ cabal sandbox init
$ cabal install -j

This will install your newly minted haq program in $PROJECT_DIR/.cabal-sandbox/bin.

Run it

And now you can run your cool project:

$ .cabal-sandbox/bin/haq me
"Haq! me"

Since our program doesn't rely on any other assets we can just copy it to a directory in our path if we want to be able to use it from anywhere without referencing the sandbox directory. Like so:

$ sudo cp .cabal-sandbox/bin/haq /usr/local/bin/haq
$ haq me
"Haq! me"

Build some haddock documentation

Generate some API documentation into dist/doc/*

Using cabal install:

$ cabal haddock

Using cabal install if you planned to upload your haq package to Hackage:
$ cabal haddock --hyperlink-source --html-location='http://hackage.haskell.org/package/haq/docs' --contents-location='http://hackage.haskell.org/package/haq'

which generates files in dist/doc/ including:

$ w3m -dump dist/doc/html/haq/Main.html
haq Contents Index
Main

Synopsis
main :: IO ()

Documentation

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.

(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
Found:
  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

to:

    where haqify = ("Haq! " ++)

Add some tests

HSpec

If you'd like to use HSpec:

To your cabal, add:

 test-suite tests
   ghc-options: -Wall
   default-extensions:  OverloadedStrings
   type: exitcode-stdio-1.0
   main-is: HSpecTests.hs
   build-depends:       base,
                        haq,
                        hspec                >= 1.8
   default-language:    Haskell2010

To enable tests and install HSpec (and any other needed dependencies):

$ cabal install --enable-tests

Then for your actual file containing the test code:

$ cat > HSpecTests.hs
module Main where

import Haq
import Test.Hspec

main :: IO ()
main = hspec $ do

  describe "Validate haqify function" $ do
    it "haqify is supposed to prefix Haq! to things" $ do
      haqify "me" `shouldBe` "Haq! me"

Execute the tests with:

$ cabal test

QuickCheck

If you're using QuickCheck:

$ cat > QuickTests.hs

import Data.Char
import Data.List
import Haq
import Test.QuickCheck
import Text.Printf

main  = mapM_ (\(s,a) -> printf "%-25s: " s >> a) tests

-- reversing twice a finite list, is the same as identity
prop_reversereverse s = (reverse . reverse) s == id s
    where _ = s :: [Int]

-- Dropping the "Haq! " string is the same as identity
prop_haq s = drop (length "Haq! ") (haqify s) == id s
    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.

Success!

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

Tag the stable version:

$ git tag 0.0

Create a tarball

You can do this using either Cabal or 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.

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

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.

Upload your package documentation to Hackage

Assuming you built your documentation like this:

$ cabal haddock --hyperlink-source --html-location='http://hackage.haskell.org/package/haq/docs' --contents-location='http://hackage.haskell.org/package/haq'

You can force upload (so you don't have to wait on the server to build it for you) your documentation for your package like so:

$ cp -R ./dist/doc/html/haq/ haq-0.0-docs $ tar cvzf --format=ustar -f haq-0.0-docs.tar.gz haq-0.0-docs $ curl -X PUT -H 'Content-Type: application/x-tar' -H 'Content-Encoding: gzip' --data-binary '@haq-0.0-docs.tar.gz' 'https://$USERNAME:$PASSWORD@hackage.haskell.org/package/haq-0.0/docs'

Summary

The following files were created:

   $ ls
   Haq.hs           HSpecTests.hs         dist             haq.cabal
   Setup.hs         .git                  haq-0.0.tar.gz   QuickTests.hs

Libraries

The process for creating a Haskell library is almost identical. The differences are as follows, for the hypothetical "ltree" library:

Hierarchical source

The source should live under a directory path that fits into the existing module layout guide. So we would create the following directory structure, for the module Data.LTree:

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

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

The Cabal file

Cabal files for libraries list the publically visible modules, and have no executable section:

   $ cat > ltree.cabal 
   Name:                ltree
   Version:             0.1
   Description:         Lambda tree implementation
   License:             BSD3
   License-file:        LICENSE
   Author:              Don Stewart
   Maintainer:          dons@cse.unsw.edu.au
   Build-Type:          Simple
   Cabal-Version:       >=1.2
   
   Library
     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...
   [1 of 1] Compiling Data.LTree       ( Data/LTree.hs, dist/build/Data/LTree.o )
   /usr/bin/ar: creating dist/build/libHSltree-0.1.a

and our library has been created as a object archive. Now install it:

   $ cabal install
   Installing: /home/dons/lib/ltree-0.1/ghc-6.6 & /home/dons/bin ltree-0.1...
   Registering ltree-0.1...
   Reading package info from ".installed-pkg-config" ... done.
   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.

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.

To have Cabal find this code, you add the following line to your Cabal file:

   hs-source-dirs: src

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

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.

Releases

It's important to release your code as stable, tagged tarballs. Don't just rely on your commit hashes for tracking history, tag your released versions and milestones!

  • git archive generates tarballs directly from a git repository based on the provided tag.

For example:

$ cd fps
$ ls       
Data      LICENSE   README    Setup.hs  TODO      .git    cbits dist      fps.cabal tests
$ git tag v0.8
$ git archive --format=tar.gz v0.8 > fps-0.8.tar.gz

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


  • Tag each release using git tag. For example:

Hosting

Hosting for repos is available from Github and the Haskell community server

There is also a (minimal) Github equivalent for Darcs at hub.darcs.net.

Web page

Create a web page documenting your project! An easy way to do this is to add a project specific page to the Haskell wiki

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.

Introductory information and build guide

A typical library user expects to:

  1. Visit Haskell.org
  2. Find the library/program they are looking for:
    1. if not found, try mailing list;
    2. if it is hidden, try improving the documentation on haskell.org;
    3. if it does not exist, try contributing code and documentation)
  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?
  • Which is the right version for given OS and Haskell implementation?
  • How is it packaged, and what tools are needed to get and unpack it?
  • How is it installed, and what tools are needed to install it?
  • How do we handle dependencies?
  • How do we provide/acquire the knowledge and tool-chains needed?

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.

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.

Program structure

Monad transformers are very useful for programming in the large, encapsulating state, and controlling side effects. To learn more about this approach, try Monad Transformers Step by Step.

Publicity

The best code in the world is meaningless if nobody knows about it. The process to follow once you've tagged and released your code is:

Join the community

If you haven't already, join the community. The best way to do this is to subscribe to at least haskell-cafe@ and haskell@ mailing lists. Joining the #haskell IRC channel is also an excellent idea.

Announce your project on haskell@

Most important: announce your project releases to the haskell@haskell.org mailing list. Tag your email subject line with "ANNOUNCE: ...". This ensure it will then make it into the Haskell Weekly News. To be doubly sure, you can email the release text to the HWN editor.

Add your code to the public collections

  • Add your library or application to the Libraries and tools page, under the relevant category, so people can find it.
  • If your library stays up to date with its dependencies, add it to the vetted packages on Stackage.

Blog about it

Blog about it! Blog about your new code on Planet Haskell. Write about your project in your blog, then email the Planet Haskell maintainer (ibid on #haskell) the RSS feed url for your blog