Difference between revisions of "How to write a Haskell program"
DonStewart (talk | contribs) (walk through again) |
(Use hierarchical module names) |
||
(97 intermediate revisions by 37 users not shown) | |||
Line 1: | Line 1: | ||
− | A guide to |
+ | 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].'' |
||
− | == Structure == |
||
+ | |||
+ | == 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 [http://git-scm.com/ git] or [http://darcs.net 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 then git or darcs are the best. Darcs hosting is available on [http://hub.darcs.net/ hub.darcs.net]. For git, [http://github.com/ github] is very popular. |
||
+ | |||
+ | This page uses darcs in the examples. |
||
+ | |||
+ | === Build system === |
||
+ | |||
+ | [[Image:Cabal-With-Text-small.png|frame|Built with Cabal]] |
||
+ | |||
+ | 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 part of the [http://haskell.org/platform Haskell Platform], so you can probably assume your users will have it too. |
||
+ | |||
+ | === Documentation === |
||
+ | |||
+ | For libraries, use [http://haskell.org/haddock/ Haddock]. We recommend |
||
+ | using the version of Haddock that ships with the Haskell Platform. Haddock generates [http://hackage.haskell.org/packages/archive/base/4.3.1.0/doc/html/Prelude.html nice markup], with links to source. |
||
+ | |||
+ | === Testing === |
||
+ | |||
+ | 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. To test impure code, use [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/HUnit HUnit]. See [http://hackage.haskell.org/packages/archive/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/2006/09/simple-unit-testing-in-haskell.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]] |
||
+ | |||
+ | === 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 === |
||
+ | |||
+ | If at all possible, depend on libraries that are provided by the [http://haskell.org/platform Haskell Platform], and libraries that in turn build against the Haskell Platform. This set of libraries is designed to be widely available, so your end users will be able to build your software. |
||
+ | |||
+ | == 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 |
[http://semantic.org/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". |
* Haq.hs -- the main haskell source file |
* Haq.hs -- the main haskell source file |
||
Line 15: | Line 62: | ||
* LICENSE -- license |
* 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. |
||
− | modules. |
||
− | Here is a transcript |
+ | Here is a transcript that shows how you'd create a minimal darcs and cabalised |
− | Haskell project |
+ | Haskell project for the cool new Haskell program "haq", build it, |
install it and release. |
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 a directory === |
||
Line 26: | Line 79: | ||
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 38: | Line 91: | ||
$ 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 50: | Line 103: | ||
</haskell> |
</haskell> |
||
− | === Stick it in |
+ | === 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): |
− | < |
+ | <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 |
+ | 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 === |
||
Create a .cabal file describing how to build your project: |
Create a .cabal file describing how to build your project: |
||
+ | <code> |
||
+ | $ cabal init |
||
+ | </code> |
||
+ | that will ask a few questions about your project and generate a file similar to the example. |
||
− | <haskell> |
||
+ | <code> |
||
− | $ cat > haq.cabal |
||
+ | -- Initial scratch.cabal generated by cabal init. For further |
||
− | Name: haq |
||
+ | -- documentation, see http://haskell.org/cabal/users-guide/ |
||
− | 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 |
||
− | + | name: haq |
|
− | + | version: 0.1.0.0 |
|
− | + | description: Super cool mega lambdas |
|
+ | license: GPL |
||
− | </haskell> |
||
+ | license-file: LICENSE |
||
+ | author: Don Stewart |
||
+ | maintainer: dons@cse.unsw.edu.au |
||
+ | build-type: Simple |
||
+ | cabal-version: >=1.10 |
||
+ | executable haq |
||
− | Add a Setup.hs that will actually do the building: |
||
+ | main-is: Haq.hs |
||
+ | build-depends: base >=4.5 && <4.6 |
||
+ | default-language: Haskell2010 |
||
+ | </code> |
||
+ | (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.) |
||
− | <haskell> |
||
− | $ cat > Setup.hs |
||
− | #!/usr/bin/env runhaskell |
||
− | import Distribution.Simple |
||
− | main = defaultMainWithHooks defaultUserHooks |
||
− | </haskell> |
||
+ | Cabal will also generate a <tt>Setup.hs</tt> file that will do the actual building. You will rarely need to modify it. |
||
− | And record your changes: |
||
+ | If you specifed a known license, it will also add a LICENSE file. |
||
− | <haskell> |
||
+ | |||
− | $ darcs add haq.cabal Setup.hs |
||
+ | You might like to add a README file to tell what your project is about. |
||
− | $ darcs record --all |
||
+ | |||
− | What is the patch name? Add a build system |
||
+ | Record your changes: |
||
− | Do you want to add a long comment? [yn]n |
||
+ | |||
− | Finished recording patch 'Add a build system' |
||
+ | <code> |
||
− | </haskell> |
||
+ | $ darcs add haq.cabal Setup.hs 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' |
||
+ | </code> |
||
=== Build your project === |
=== 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. |
||
− | Now build it! |
||
+ | Building using cabal-install: |
||
− | <haskell> |
||
+ | |||
− | $ runhaskell Setup.hs configure --prefix=/home/dons |
||
+ | <code> |
||
− | $ runhaskell Setup.hs build |
||
+ | $ cabal install --prefix=$HOME --user |
||
− | $ runhaskell Setup.hs install |
||
− | </ |
+ | </code> |
+ | |||
+ | Building using the traditional Setup.hs method: |
||
+ | |||
+ | <code> |
||
+ | $ runhaskell Setup configure --prefix=$HOME --user |
||
+ | $ runhaskell Setup build |
||
+ | $ runhaskell Setup install |
||
+ | </code> |
||
+ | |||
+ | This will install your newly minted haq program in $HOME/bin. |
||
=== Run it === |
=== Run it === |
||
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 |
+ | 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 === |
||
− | Generate some |
+ | Generate some API documentation into dist/doc/* |
+ | Using cabal install: |
||
− | <haskell> |
||
+ | <code> |
||
− | $ runhaskell Setup.hs haddock |
||
+ | $ cabal haddock |
||
− | </haskell> |
||
+ | </code> |
||
+ | |||
+ | Traditional method: |
||
+ | <code> |
||
+ | $ runhaskell Setup 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 172: | Line 248: | ||
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. |
||
+ | |||
+ | === (Optional) Improve your code: HLint === |
||
+ | |||
+ | [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. |
||
+ | |||
+ | <code> |
||
+ | $ hlint . |
||
+ | ./Haq.hs:11:1: Warning: Eta reduce |
||
+ | Found: |
||
+ | haqify s = "Haq! " ++ s |
||
+ | Why not: |
||
+ | haqify = ("Haq! " ++) |
||
+ | </code> |
||
+ | |||
+ | The existing code will work, but let's follow that suggestion. Open Haq.hs in your favourite editor and change the line: |
||
+ | |||
+ | <haskell> |
||
+ | where haqify s = "Haq! " ++ s |
||
+ | </haskell> |
||
+ | |||
+ | to: |
||
+ | |||
+ | <haskell> |
||
+ | where haqify = ("Haq! " ++) |
||
</haskell> |
</haskell> |
||
=== Add some automated testing: QuickCheck === |
=== Add some automated testing: QuickCheck === |
||
+ | ==== QuickCheck v1 ==== |
||
− | We'll use QuickCheck to specify a simple property of our Haq.hs code. So |
||
+ | |||
− | create a tests module, Tests.hs, with some QuickCheck boilerplate: |
||
+ | We'll use QuickCheck to specify a simple property of our Haq.hs code. Create a tests module, Tests.hs, with some QuickCheck boilerplate: |
||
<haskell> |
<haskell> |
||
$ cat > Tests.hs |
$ cat > Tests.hs |
||
− | import Char |
+ | import Data.Char |
− | import List |
+ | import Data.List |
import Test.QuickCheck |
import Test.QuickCheck |
||
import Text.Printf |
import Text.Printf |
||
Line 207: | Line 311: | ||
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 225: | Line 329: | ||
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! |
||
+ | ==== QuickCheck v2 ==== |
||
− | === Running the testsuite from darcs === |
||
+ | If you're using version 2 of QuickCheck, the code in the previous section needs some minor modifications: |
||
− | We can arrange for darcs to run the testsuite on every commit: |
||
<haskell> |
<haskell> |
||
− | $ |
+ | $ cat > Tests.hs |
+ | import Data.Char |
||
− | </haskell> |
||
+ | import Data.List |
||
+ | 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 |
||
− | will run the full set of QuickChecks. Let's commit a new patch: |
||
+ | prop_reversereverse s = (reverse . reverse) s == id s |
||
+ | where _ = s :: [Int] |
||
+ | -- Dropping the "Haq! " string is the same as identity |
||
− | <haskell> |
||
+ | prop_haq s = drop (length "Haq! ") (haqify s) == id s |
||
− | $ darcs setpref test "runhaskell Tests.hs" |
||
+ | where haqify s = "Haq! " ++ s |
||
− | Changing value of test from '' to 'runhaskell Tests.hs' |
||
+ | |||
− | $ darcs add Tests.hs |
||
+ | tests = [("reverse.reverse/id", quickCheck prop_reversereverse) |
||
− | $ darcs record --all |
||
+ | ,("drop.haq/id", quickCheck prop_haq)] |
||
− | 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' |
||
</haskell> |
</haskell> |
||
+ | To run the test: |
||
− | Excellent, now patches must pass the testsuite before they can be |
||
+ | |||
− | committed. |
||
+ | <code> |
||
+ | $ runhaskell Tests.hs |
||
+ | reverse.reverse/id : +++ OK, passed 100 tests. |
||
+ | drop.haq/id : +++ OK, passed 100 tests. |
||
+ | </code> |
||
+ | |||
+ | Success! |
||
+ | |||
+ | === Running the test suite from darcs === |
||
+ | |||
+ | We can arrange for darcs to run the test suite on every commit that is run with the flag --test: |
||
+ | |||
+ | <code> |
||
+ | $ darcs setpref test "runhaskell Tests.hs" |
||
+ | Changing value of test from '' to 'runhaskell Tests.hs' |
||
+ | </code> |
||
+ | |||
+ | will run the full set of QuickChecks. |
||
+ | If your test requires it, you may need to ensure other things are built too -- for example:<code>darcs setpref test "alex Tokens.x;happy Grammar.y;runhaskell Tests.hs"</code>. |
||
+ | You will encounter that this way a darcs patch is also accepted if a QuickCheck test fails. |
||
+ | You have two choices to [http://www.haskell.org/pipermail/haskell-cafe/2007-October/033834.html work around] this: |
||
+ | * Use <hask>quickCheck'</hask> from the package QuickCheck-2 and call <hask>exitWithFailure</hask> if it return <hask>False</hask>. |
||
+ | * Keep the test program as it is, and implement the failure on the shell level: |
||
+ | : <code>runhaskell Tests.hs | tee test.log && if grep Falsifiable test.log >/dev/null; then exit 1; fi</code> |
||
+ | |||
+ | Let's commit a new patch: |
||
+ | |||
+ | <code> |
||
+ | $ darcs add Tests.hs |
||
+ | $ darcs record --all --test |
||
+ | 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' |
||
+ | </code> |
||
+ | |||
+ | Excellent: now, patches must pass the test suite before they can be committed provided the --test flag is passed. |
||
=== Tag the stable version, create a tarball, and sell it! === |
=== Tag the stable version, create a tarball, and sell it! === |
||
Line 265: | Line 410: | ||
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> |
− | + | ==== Create a tarball ==== |
|
+ | You can do this using either Cabal or darcs, or even an explicit <tt>tar</tt> command. |
||
− | <haskell> |
||
+ | |||
− | $ darcs dist -d haq-0.0 |
||
+ | ===== Using Cabal ===== |
||
− | Created dist as haq-0.0.tar.gz |
||
+ | |||
− | </haskell> |
||
+ | Since the code is cabalised, we can create a tarball with cabal-install |
||
+ | 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]): |
||
+ | |||
+ | <code> |
||
+ | $ 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 |
||
+ | 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 <tt>Test.hs</tt> in the above example, and our README), we need to add: |
||
+ | |||
+ | <code> |
||
+ | extra-source-files: Tests.hs README |
||
+ | </code> |
||
+ | |||
+ | to the .cabal file to have everything included. |
||
+ | |||
+ | ===== Using darcs ===== |
||
+ | |||
+ | Alternatively, you can use darcs: |
||
+ | <code> |
||
+ | $ darcs dist -d haq-0.0 |
||
+ | Created dist as haq-0.0.tar.gz |
||
+ | </code> |
||
And you're all set up! |
And you're all set up! |
||
+ | |||
+ | ==== Check that your source package is complete ==== |
||
+ | |||
+ | Just to make sure everything works, try building the source package in some temporary directory: |
||
+ | <code> |
||
+ | $ tar xzf haq-0.0.tar.gz |
||
+ | $ cd haq-0.0 |
||
+ | $ 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. |
||
+ | |||
+ | === Summary === |
||
+ | |||
+ | The following files were created: |
||
+ | |||
+ | $ ls |
||
+ | Haq.hs Tests.hs dist haq.cabal |
||
+ | Setup.hs _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 [[Hierarchical module names|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 |
||
+ | [http://www.haskell.org/cabal/users-guide/ Cabal user guide]. |
||
== Licenses == |
== Licenses == |
||
Line 284: | Line 564: | ||
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://www.dina.dk/~abraham/rants/license.html this]). |
||
− | Check the licences of things you use |
+ | 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 |
+ | split into 2 camps, roughly: those who release everything under BSD, and |
− | (L)GPLers. Some Haskellers recommend avoiding LGPL, due to cross |
+ | (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 |
+ | cross-module optimisation problem. |
− | |||
− | == Revision control == |
||
− | |||
− | Use [http://darcs.net Darcs] unless you have a specific reason not to. |
||
− | Almost all new Haskell projects are released under Darcs, and this |
||
− | benefits everyone -- a set of common tools increases productivity, and |
||
− | you're more likely to get patches. |
||
− | |||
− | Advice: |
||
− | * Tag each release |
||
== 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 [http:// |
+ | just [http://jackunrue.blogspot.com/2006/11/don-do-releases.html rely on darcs for distribution]. |
* '''darcs dist''' generates tarballs directly from a darcs repository |
* '''darcs dist''' generates tarballs directly from a darcs repository |
||
Line 318: | Line 588: | ||
Created dist as fps-0.8.tar.gz |
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. |
You can also have darcs do the equivalent of 'daily snapshots' for you by using a post-hook. |
||
Line 325: | Line 595: | ||
apply posthook darcs dist |
apply posthook darcs dist |
||
apply run-posthook |
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 == |
||
+ | Hosting for repos is available from the Haskell community server: |
||
− | A Darcs repository can be publised simply by making it available from a |
||
− | 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. |
||
+ | http://community.haskell.org/ |
||
− | There are also many free hosting places for open source, such as |
||
+ | |||
− | * [http://code.google.com/hosting/ Google Project Hosting] |
||
+ | A Darcs repository can be published simply by making it available from a |
||
− | * [http://sourceforge.net/ SourceForge]. |
||
+ | web page. |
||
+ | |||
+ | There is also a (minimal) Github equivalent for Darcs at [http://hub.darcs.net/ hub.darcs.net]. |
||
== 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 [[ |
+ | 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. |
||
− | Use [http://haskell.org/cabal Cabal]. |
||
+ | === Introductory information and build guide === |
||
− | === Example Setup === |
||
− | Create a file called Setup.lhs with these contents: |
||
− | <haskell> |
||
− | #!/usr/bin/env runhaskell |
||
+ | A typical library user expects to: |
||
− | > import Distribution.Simple |
||
− | > main = defaultMain |
||
+ | # Visit [[Haskell|Haskell.org]] |
||
− | </haskell> |
||
+ | # Find the library/program they are looking for: |
||
− | Writing the setup file this way allows it to be executed directly by unix shells. |
||
+ | ## 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 |
||
− | === Example cabal file for Executables === |
||
+ | 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 |
||
− | Create the file myproject.cabal following this example: |
||
+ | following questions should have clear answers: |
||
− | <pre> |
||
− | Name: MyProject |
||
− | Version: 0.1 |
||
− | License: BSD3 |
||
− | Author: Your Name |
||
− | Build-Depends: base |
||
− | Synopsis: Example Cabal Executable File |
||
+ | * Which is the latest version? |
||
− | Executable: myproj |
||
+ | * What state is it in? |
||
− | Main-Is: Main.hs |
||
+ | * What are its aims? |
||
− | Other-Modules: Foo |
||
+ | * Where is the documentation? |
||
− | </pre> |
||
+ | * 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, |
||
− | === Example cabal file for Libraries === |
||
+ | distributed with the library or application, and often accompanied with |
||
− | Create the file myproject.cabal following this example: |
||
+ | similar text on a more extensive web page. |
||
− | <pre> |
||
− | Name: MyProj |
||
− | Version: 0.1 |
||
− | License: BSD3 |
||
− | Author: Your Name |
||
− | Build-Depends: base |
||
− | Synopsis: Example Cabal Library File |
||
− | Exposed-Modules: MyProject.Foo |
||
− | </pre> |
||
− | == |
+ | === Tutorials === |
+ | Generated haddock documentation is usually not enough to help new |
||
− | Use [http://haskell.org/haddock Haddock]. |
||
+ | 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. |
||
− | == 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]. |
||
− | |||
− | To get started try, [[Introduction to QuickCheck]]. For a slightly more advanced introduction, here is a blog article about creating a testing framework for QuickCheck using some Template Haskell, [http://blog.codersbase.com/2006/09/01/simple-unit-testing-in-haskell/ Simple Unit Testing in Haskell]. |
||
== 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:// |
+ | 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 == |
||
− | The best code in the world is meaningless if nobody knows about it |
+ | 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 [http://haskell.org/haskellwiki/Mailing_lists subscribe] to at least haskell-cafe@ and haskell@ mailing lists. Joining the [[IRC_channel|#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 [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 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] (Haskell's CPAN wanna-be). |
||
+ | |||
+ | === Blog about it === |
||
+ | Blog about it! Blog about your new code on [http://planet.haskell.org Planet Haskell]. |
||
− | * Firstly, join the community! [http://haskell.org/haskellwiki/Mailing_lists Subscribe] to at least haskell-cafe@ and haskell@ mailing lists. |
||
− | + | Write about your project in your blog, then email the [http://planet.haskell.org/ Planet Haskell] maintainer (ibid on [[IRC channel|#haskell]]) the RSS feed url for your blog |
|
− | * Blog about it, on [http://planet.haskell.org Planet Haskell] |
||
− | ** Write about it on your blog |
||
− | ** Then email the [http://planet.haskell.org/ Planet Haskell] maintainer (ibid on [[IRC channel|#haskell]]) the RSS feed url for your blog |
||
− | * Add your library or tool to the [[Libraries and tools]] page, under the relevant category, so people can find it. |
||
[[Category:Community]] |
[[Category:Community]] |
Revision as of 13:03, 14 March 2014
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 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 then git or darcs are the best. Darcs hosting is available on hub.darcs.net. For git, github is very popular.
This page uses darcs in the examples.
Build system
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 part of the Haskell Platform, so you can probably assume your users will have it too.
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.
Testing
You can use QuickCheck or SmallCheck to test pure code. To test impure code, use HUnit. 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
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 at all possible, depend on libraries that are provided by the Haskell Platform, and libraries that in turn build against the Haskell Platform. This set of libraries is designed to be widely available, so your end users will be able to build your software.
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
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.
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):
$ 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:
$ 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.6
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:
$ darcs add haq.cabal Setup.hs 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! 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.
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/*
Using cabal install:
$ cabal haddock
Traditional method:
$ runhaskell Setup 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.
(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 automated testing: QuickCheck
QuickCheck v1
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 Data.Char
import Data.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!
QuickCheck v2
If you're using version 2 of QuickCheck, the code in the previous section needs some minor modifications:
$ cat > Tests.hs
import Data.Char
import Data.List
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!
Running the test suite from darcs
We can arrange for darcs to run the test suite on every commit that is run with the flag --test:
$ 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
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 --test
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 provided the --test flag is passed.
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'
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!
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.
Summary
The following files were created:
$ ls Haq.hs Tests.hs dist haq.cabal Setup.hs _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-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 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.
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:
- 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. 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 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