How to write a Haskell program: Difference between revisions
(let 'darcs record' fail if one of the tests fail) |
(use <code> instead of <haskell> for non-Haskell code) |
||
Line 69: | Line 69: | ||
Create somewhere for the source: | Create somewhere for the source: | ||
< | <code> | ||
$ mkdir haq | $ mkdir haq | ||
$ cd haq | $ cd haq | ||
</ | </code> | ||
=== Write some Haskell source === | === Write some Haskell source === | ||
Line 97: | Line 97: | ||
Place the source under revision control (you may need to enter your e-mail address first, to identify you as maintainer of this source): | Place the source under revision control (you may need to enter your e-mail address first, to identify you as maintainer of this source): | ||
< | <code> | ||
$ darcs init | $ darcs init | ||
$ darcs add Haq.hs | $ darcs add Haq.hs | ||
$ darcs record | $ darcs record | ||
addfile ./Haq.hs | addfile ./Haq.hs | ||
Shall I record this change? (1/?) [ynWsfqadjkc], or ? for help: y | Shall I record this change? (1/?) [ynWsfqadjkc], or ? for help: y | ||
hunk ./Haq.hs 1 | hunk ./Haq.hs 1 | ||
+-- | +-- | ||
+-- 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) | ||
+-- | +-- | ||
+import System.Environment | +import System.Environment | ||
+ | + | ||
+-- | 'main' runs the main program | +-- | 'main' runs the main program | ||
+main :: IO () | +main :: IO () | ||
+main = getArgs >>= print . haqify . head | +main = getArgs >>= print . haqify . head | ||
+ | + | ||
+haqify s = "Haq! " ++ s | +haqify s = "Haq! " ++ s | ||
Shall I record this change? (2/?) [ynWsfqadjkc], or ? for help: y | Shall I record this change? (2/?) [ynWsfqadjkc], or ? for help: y | ||
What is the patch name? Import haq source | What is the patch name? Import haq source | ||
Do you want to add a long comment? [yn]n | Do you want to add a long comment? [yn]n | ||
Finished recording patch 'Import haq source' | Finished recording patch 'Import haq source' | ||
</ | </code> | ||
And we can see that darcs is now running the show: | And we can see that darcs is now running the show: | ||
< | <code> | ||
$ ls | $ ls | ||
Haq.hs _darcs | Haq.hs _darcs | ||
</ | </code> | ||
=== Add a build system === | === Add a build system === | ||
Line 132: | Line 132: | ||
Create a .cabal file describing how to build your project: | Create a .cabal file describing how to build your project: | ||
< | <code> | ||
$ cat > haq.cabal | $ cat > haq.cabal | ||
Name: haq | Name: haq | ||
Version: 0.0 | Version: 0.0 | ||
Description: Super cool mega lambdas | Description: Super cool mega lambdas | ||
License: GPL | License: GPL | ||
License-file: LICENSE | License-file: LICENSE | ||
Author: Don Stewart | Author: Don Stewart | ||
Maintainer: dons@cse.unsw.edu.au | Maintainer: dons@cse.unsw.edu.au | ||
Build-Depends: base | Build-Depends: base | ||
Executable: haq | Executable: haq | ||
Main-is: Haq.hs | Main-is: Haq.hs | ||
ghc-options: -O | ghc-options: -O | ||
</ | </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>haskell98</tt>, you'll need to add them to the <tt>Build-Depends:</tt> field.) | ||
Line 164: | Line 164: | ||
Record your changes: | Record your changes: | ||
< | <code> | ||
$ darcs add haq.cabal Setup.lhs LICENSE README | $ darcs add haq.cabal Setup.lhs LICENSE README | ||
$ darcs record --all | $ darcs record --all | ||
What is the patch name? Add a build system | What is the patch name? Add a build system | ||
Do you want to add a long comment? [yn]n | Do you want to add a long comment? [yn]n | ||
Finished recording patch 'Add a build system' | Finished recording patch 'Add a build system' | ||
</ | </code> | ||
=== Build your project === | === Build your project === | ||
Line 176: | Line 176: | ||
Now build it! | Now build it! | ||
< | <code> | ||
$ runhaskell Setup.lhs configure --prefix=$HOME | $ runhaskell Setup.lhs configure --prefix=$HOME | ||
$ runhaskell Setup.lhs build | $ runhaskell Setup.lhs build | ||
$ runhaskell Setup.lhs install | $ runhaskell Setup.lhs install | ||
</ | </code> | ||
This will install your newly minted haq program in $HOME/bin. | This will install your newly minted haq program in $HOME/bin. | ||
Line 187: | Line 187: | ||
And now you can run your cool project: | And now you can run your cool project: | ||
< | <code> | ||
$ haq me | $ haq me | ||
"Haq! me" | "Haq! me" | ||
</ | </code> | ||
You can also run it in-place, even if you skip the install phase: | You can also run it in-place, even if you skip the install phase: | ||
< | <code> | ||
$ dist/build/haq/haq you | $ dist/build/haq/haq you | ||
"Haq! you" | "Haq! you" | ||
</ | </code> | ||
=== Build some haddock documentation === | === Build some haddock documentation === | ||
Line 202: | Line 202: | ||
Generate some API documentation into dist/doc/* | Generate some API documentation into dist/doc/* | ||
< | <code> | ||
$ runhaskell Setup.lhs haddock | $ runhaskell Setup.lhs haddock | ||
</ | </code> | ||
which generates files in dist/doc/ including: | which generates files in dist/doc/ including: | ||
< | <code> | ||
$ w3m -dump dist/doc/html/haq/Main.html | $ w3m -dump dist/doc/html/haq/Main.html | ||
haq Contents Index | haq Contents Index | ||
Line 222: | Line 222: | ||
Produced by Haddock version 0.7 | Produced by Haddock version 0.7 | ||
</ | </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. | 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. | ||
Line 258: | Line 258: | ||
We can now run this test, and have QuickCheck generate the test data: | We can now run this test, and have QuickCheck generate the test data: | ||
< | <code> | ||
$ runhaskell Tests.hs | $ runhaskell Tests.hs | ||
reverse.reverse/id : OK, passed 100 tests. | reverse.reverse/id : OK, passed 100 tests. | ||
</ | </code> | ||
Let's add a test for the 'haqify' function: | Let's add a test for the 'haqify' function: | ||
Line 276: | Line 276: | ||
and let's test that: | and let's test that: | ||
< | <code> | ||
$ runhaskell Tests.hs | $ runhaskell Tests.hs | ||
reverse.reverse/id : OK, passed 100 tests. | reverse.reverse/id : OK, passed 100 tests. | ||
drop.haq/id : OK, passed 100 tests. | drop.haq/id : OK, passed 100 tests. | ||
</ | </code> | ||
Great! | Great! | ||
Line 288: | Line 288: | ||
We can arrange for darcs to run the test suite on every commit: | We can arrange for darcs to run the test suite on every commit: | ||
< | <code> | ||
$ darcs setpref test "runhaskell Tests.hs" | $ darcs setpref test "runhaskell Tests.hs" | ||
Changing value of test from '' to 'runhaskell Tests.hs' | Changing value of test from '' to 'runhaskell Tests.hs' | ||
</ | </code> | ||
will run the full set of QuickChecks. | will run the full set of QuickChecks. | ||
Line 303: | Line 303: | ||
Let's commit a new patch: | Let's commit a new patch: | ||
< | <code> | ||
$ darcs add Tests.hs | $ darcs add Tests.hs | ||
$ darcs record --all | $ darcs record --all | ||
What is the patch name? Add testsuite | What is the patch name? Add testsuite | ||
Do you want to add a long comment? [yn]n | Do you want to add a long comment? [yn]n | ||
Running test... | Running test... | ||
reverse.reverse/id : OK, passed 100 tests. | reverse.reverse/id : OK, passed 100 tests. | ||
drop.haq/id : OK, passed 100 tests. | drop.haq/id : OK, passed 100 tests. | ||
Test ran successfully. | Test ran successfully. | ||
Looks like a good patch. | Looks like a good patch. | ||
Finished recording patch 'Add testsuite' | Finished recording patch 'Add testsuite' | ||
</ | </code> | ||
Excellent, now patches must pass the test suite before they can be committed. | Excellent, now patches must pass the test suite before they can be committed. | ||
Line 322: | Line 322: | ||
Tag the stable version: | Tag the stable version: | ||
< | <code> | ||
$ darcs tag | $ darcs tag | ||
What is the version name? 0.0 | What is the version name? 0.0 | ||
Finished tagging patch 'TAG 0.0' | Finished tagging patch 'TAG 0.0' | ||
</ | </code> | ||
==== Tarballs via Cabal ==== | ==== Tarballs via Cabal ==== | ||
Line 333: | Line 333: | ||
directly: | directly: | ||
< | <code> | ||
$ runhaskell Setup.lhs sdist | $ runhaskell Setup.lhs sdist | ||
Building source dist for haq-0.0... | Building source dist for haq-0.0... | ||
Source tarball created: dist/haq-0.0.tar.gz | 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 expected by HackageDB. | ||
Line 343: | Line 343: | ||
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: | 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: | ||
< | <code> | ||
extra-source-files: Tests.hs README | extra-source-files: Tests.hs README | ||
</ | </code> | ||
to the .cabal file to have everything included. | to the .cabal file to have everything included. | ||
Line 352: | Line 352: | ||
Alternatively, you can use darcs: | Alternatively, you can use darcs: | ||
< | <code> | ||
$ darcs dist -d haq-0.0 | $ darcs dist -d haq-0.0 | ||
Created dist as haq-0.0.tar.gz | Created dist as haq-0.0.tar.gz | ||
</ | </code> | ||
And you're all set up! | And you're all set up! | ||
Line 457: | Line 457: | ||
Usage is: | Usage is: | ||
< | <code> | ||
$ mkcabal | $ mkcabal | ||
Project name: haq | Project name: haq | ||
What license ["GPL","LGPL","BSD3","BSD4","PublicDomain","AllRightsReserved"] ["BSD3"]: | What license ["GPL","LGPL","BSD3","BSD4","PublicDomain","AllRightsReserved"] ["BSD3"]: | ||
What kind of project [Executable,Library] [Executable]: | What kind of project [Executable,Library] [Executable]: | ||
Is this your name? - "Don Stewart " [Y/n]: | Is this your name? - "Don Stewart " [Y/n]: | ||
Is this your email address? - "<dons@cse.unsw.edu.au>" [Y/n]: | Is this your email address? - "<dons@cse.unsw.edu.au>" [Y/n]: | ||
Created Setup.lhs and haq.cabal | Created Setup.lhs and haq.cabal | ||
$ ls | $ ls | ||
Haq.hs LICENSE Setup.lhs _darcs dist haq.cabal | Haq.hs LICENSE Setup.lhs _darcs dist haq.cabal | ||
</ | </code> | ||
which will fill out some stub Cabal files for the project 'haq'. | which will fill out some stub Cabal files for the project 'haq'. | ||
Line 473: | Line 473: | ||
To create an entirely new project tree: | To create an entirely new project tree: | ||
< | <code> | ||
$ mkcabal --init-project | $ mkcabal --init-project | ||
Project name: haq | Project name: haq | ||
What license ["GPL","LGPL","BSD3","BSD4","PublicDomain","AllRightsReserved"] ["BSD3"]: | What license ["GPL","LGPL","BSD3","BSD4","PublicDomain","AllRightsReserved"] ["BSD3"]: | ||
What kind of project [Executable,Library] [Executable]: | What kind of project [Executable,Library] [Executable]: | ||
Is this your name? - "Don Stewart " [Y/n]: | Is this your name? - "Don Stewart " [Y/n]: | ||
Is this your email address? - "<dons@cse.unsw.edu.au>" [Y/n]: | Is this your email address? - "<dons@cse.unsw.edu.au>" [Y/n]: | ||
Created new project directory: haq | Created new project directory: haq | ||
$ cd haq | $ cd haq | ||
$ ls | $ ls | ||
Haq.hs LICENSE README Setup.lhs haq.cabal | Haq.hs LICENSE README Setup.lhs haq.cabal | ||
</ | </code> | ||
== Licenses == | == Licenses == |
Revision as of 22:42, 4 November 2007
A guide to the best practice for creating a new Haskell project or program.
Recommended tools
Almost all new Haskell projects use the following tools. 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.
Revision control
Use Darcs unless you have a specific reason not to. It's much more powerful than most competing systems (and it's written in Haskell).
Build system

Use Cabal. You should read at least the start of section 2 of the Cabal User's Guide.
Documentation
For libraries, use Haddock. We recommend using the latest version of haddock (currently 0.8).
Testing
Pure code can be tested using QuickCheck or SmallCheck, impure code with 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.
Distribution
The new 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.
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
- _darcs -- revision control
- README -- info
- LICENSE -- license
You can of course 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 on 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 'mkcabal' automates all this for you, but its important to understand all the parts first.
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 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
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'
And we can see that darcs is now running the show:
$ ls
Haq.hs _darcs
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
Maintainer: dons@cse.unsw.edu.au
Build-Depends: base
Executable: haq
Main-is: Haq.hs
ghc-options: -O
(If your package uses other packages, e.g. haskell98, you'll need to add them to the Build-Depends: field.) Add a Setup.lhs that will actually do the building:
$ cat > Setup.lhs
#! /usr/bin/env runhaskell
> import Distribution.Simple
> main = defaultMain
Cabal allows either Setup.hs or Setup.lhs, but we recommend writing the setup file this way so that it can be executed directly by Unix shells.
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.lhs LICENSE README
$ darcs record --all
What is the patch name? Add a build system
Do you want to add a long comment? [yn]n
Finished recording patch 'Add a build system'
Build your project
Now build it!
$ runhaskell Setup.lhs configure --prefix=$HOME
$ runhaskell Setup.lhs build
$ runhaskell Setup.lhs install
This will install your newly minted haq program in $HOME/bin.
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"
Build some haddock documentation
Generate some API documentation into dist/doc/*
$ runhaskell Setup.lhs haddock
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.
Add some automated testing: QuickCheck
We'll use QuickCheck to specify a simple property of our Haq.hs code. Create a tests module, Tests.hs, with some QuickCheck boilerplate:
$ cat > Tests.hs
import Char
import List
import Test.QuickCheck
import Text.Printf
main = mapM_ (\(s,a) -> printf "%-25s: " s >> a) tests
instance Arbitrary Char where
arbitrary = choose ('\0', '\128')
coarbitrary c = variant (ord c `rem` 4)
Now let's write a simple property:
$ cat >> Tests.hs
-- 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)]
We can now run this test, and have QuickCheck generate the test data:
$ runhaskell Tests.hs
reverse.reverse/id : OK, passed 100 tests.
Let's add a test for the 'haqify' function:
-- 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)
,("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.
Great!
Running the test suite from darcs
We can arrange for darcs to run the test suite on every commit:
$ 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 eg: 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
quickCheck'
from the package QuickCheck-2 and callexitWithFailure
if it returnFalse
. - 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
Do you want to add a long comment? [yn]n
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.
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'
Tarballs via Cabal
Since the code is cabalised, we can create a tarball with Cabal directly:
$ runhaskell Setup.lhs 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 expected by HackageDB.
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.
Tarballs via 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!
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.
Summary
The following files were created:
$ ls Haq.hs Tests.hs dist haq.cabal Setup.lhs _darcs haq-0.0.tar.gz
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-Depends: base Exposed-modules: Data.LTree ghc-options: -Wall -O
We can thus build our library:
$ runhaskell Setup.lhs configure --prefix=$HOME $ runhaskell Setup.lhs 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:
$ runhaskell Setup.lhs 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! You can 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 it is useful to have source trees stored 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
Cabal can set up to also run configure scripts, along with a range of other features. For more information consult the 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:
$ 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
which will fill out some stub Cabal files for the project 'haq'.
To create an entirely new project tree:
$ 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
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 thing.
Releases
It's important to release your code as stable, tagged tarballs. Don't just rely on darcs for distribution.
- 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
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:
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 darcs pull --partial -t 0.8, to get just the tagged version (and not the entire history).
Hosting
Hosting for repos is available from the Haskell community server:
http://community.haskell.org/
A Darcs repository can be published simply by making it available from a web page.
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 keep in mind the user's expectations for how the library is to be built and used.
Introductory information and build guide
The intended approach for a user of a library is roughly:
- Visit 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. Even if steps 1..2 are successful, and ensuring step 5 is the main concern of code authors and users, it is often steps 3..4 that 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 with 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 not enough, usually, for new programmers to learn how to use a library. It is critical to also provide accompanying examples, and even tutorials on the use of the library.
Please consider providing example, type correct code, with commentary, on the use of your library (or application).
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 release is a Cabal package, add it to the Hackage database (Haskell's CPAN wanna-be).
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
Example
A complete example of writing, packaging and releasing a new Haskell library under this process has been documented.