How to Start a New Haskell Project

| Comments

I’m working on a Haskell project at the moment, I guess my first Haskell client work. It’s a lot of fun, and a little bit different from the more loosely typed languages I’ve used before.

So, Haskell. Not as hard as you’d think to write something in it, though like with any language, I suspect it will take one a while to write it really well. Even as a beginner, it’s actually surprisingly easy to write expressive and friendly code.

I was intending to write a “getting started” for haskell projects (not including basic knowledge of the language’s syntax and philosophy), and then found that other people had done the same job perfectly well, so I’ll refer to them first.

I’ll still add points that weren’t covered much in other posts and I had to cobble together for myself, looking at other people’s code and various blog posts and stack overflow questions.

Useful references

Cabal project

Cabal leaves you a fair bit of freedom on how to organize your code, as long as you edit the cabal file to let them know where to find it. Repeating slightly what the blog posts pointed to above said:

1
➜  new-project  cabal init

will produce a cabal file and some bits and bobs, but not much in the way of directory structure.

Browsing around and looking at other open source project, the cleanest minimum seems to me to be as follows:

1
2
3
4
5
6
7
➜  new-project  tree
.
|-- LICENSE
|-- Setup.hs
|-- new-project.cabal
|-- src
`-- test

With a code directory (src) and a test directory (test). Now you need to give cabal this information: under “library” or “executable”:

1
2
library
  hs-source-dirs: src

and for the tests:

1
2
test-suite Tests
  hs-source-dirs: test

Duh, you’ll say. Well, a few OS projects I looked at dumpled the code in the root folder, which is cabal’s default, but I find this a bit messy. YMMV.

Edit:: Daisuke Fujimura pointed me to his project template generator here. You can also go quite a bit further by using this structure, which to me is slightly over the top.

Cabal Sandboxex

(Edit) Cabal sandboxes allow you to shield yourself from dependency hell to a certain extent, by having a sandbox in which you’ll install only the dependencies of your project: read more here

Namespaces

Those of you who have done java before will groan: basically same principle. Maybe something in the air of the 90s. Namespaces will be placed in a directory structure that corresponds to the name.

Say your namespace is Carbs.Pasta.Linguine (notice the capitalized names), the code will need to be under src/Carbs/Pasta/Linguine.hs

Tests

I’ve heard some people talk about the fact that statically typed and compiled languages like Haskell don’t need tests. I tend to view such statements with suspicion, since it seems to me that type checking will eliminate some, but not all possible forms of human error. The fact that there is good tooling for testing in Haskell tends to support my view.

Like any language, Haskell has a range of test libraries to offer. You can do the simple assertion thing, with HUnit, or a more BDD like style, with HSpec. Both will have familiar syntax.

Cabal allows you to specify how to run your tests in the cabal file. For a test suite (you can have more than one) running HUnit:

1
2
3
4
5
6
7
8
9
10
11
test-suite Tests
  hs-source-dirs: test
  main-is: tests.hs
  Type: exitcode-stdio-1.0
  build-depends:       base ==4.6.*, Cabal >= 1.16.0
                       , your-project
                       , QuickCheck
                       , HUnit
                       , test-framework
                       , test-framework-hunit
                       , test-framework-quickcheck2

The dependencies include your own project, and any test libraries you choose to use. Here I’m using test-framework, which adds several good features to basic hunit and quickcheck tests. The type specification exists for backwards compatibility purposes, it’s recommended to use the detailed-0.9 or detailed-1.0 interface, but exitstdio is the one that works most painlessly for me at the moment. To be revisited in future projects.

Small gotcha for tests: you need to expose the necessary namespaces you want your tests to use in your cabal file. Otherwise the linker (ld) will complain with scary-looking errors. Under your library or executable spec in the cabal file:

1
2
  Exposed-modules:      Carbs.Pasta.Linguine
                        Carbs.Pasta.Vongole

This will allow you to use the task ‘cabal test’.

Then you can define several suites for your different namespaces:

1
2
3
4
5
6
7
8
9
module Main where

import Test.Framework (defaultMain)

import Carbs.Pasta.Linguine.Test
import Carbs.Pasta.Vongole.Test

main :: IO ()
main = defaultMain [linguineSuite, vongoleSuite]

Tests themselves, using HUnit, are structured same as namespaces (test/Carbs/Pasta/Linguine/Test.hs). It often makes sense to define custom test functions to make the test statements concise (using Assertion producing functions specified in HUnit).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module Carbs.Pasta.Linguine.Test where

import Test.Framework (testGroup, Test)
import Test.Framework.Providers.HUnit
import Test.HUnit hiding (Test)

import Carbs.Pasta.Linguine

linguineSuite :: Test
linguineSuite = testGroup "Linguine cooking"
   [testCase "cooking linguine takes 10 minutes" (testCook Linguine 10)]

testCook :: Pasta -> Int -> Assertion
testCook pasta expected = expected @=? cook pasta

(HSpec will be familiar for those who know RSpec, if you prefer the style).

QuickCheck

QuickCheck is one of Haskell’s flagship tools. QuickCheck will allow you to test a function with a number of randomly generated input to prove properties your function should always have (invariants), effectively producing combinatorial numbers of test for free – including edge cases.

Again, I believe that nothing is foolproof: the quality of your QuickCheck tests will depend on the quality of your assumptions as related to the problem you want to solve. But then QuickCheck allows you to test those assumptions to the hilt.

Let’s go with a toy example. Say we have a function that counts the kilocalories in your vongole dish.

1
2
3
4
type IngredientCaloryCount = Int

calories :: [IngredientCaloryCount] -> Int
calories ingredients = foldr (+) 100 ingredients

The property I want to prove is that every calory count will be higher than the basic 100, which is the assumed number for a portion of the pasta.

Our property, and test, will look as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
module Carbs.Pasta.Vongole.Test where

import Test.Framework (testGroup, Test)
import Test.Framework.Providers.QuickCheck2 (testProperty)

import Carbs.Pasta.Vongole

vongoleSuite :: Test
vongoleSuite = testGroup "Vongole"
   [ testProperty "calory count >= 100" prop_minimumCalories ]

prop_minimumCalories :: [Int] -> Bool
prop_minimumCalories list = calories list >= 100

This test fails very quickly.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Running 1 test suites...
Test suite cooking: RUNNING...
Linguine cooking:
  cooking linguine takes 10 minutes: [OK]
Vongole:
  calory count >= 100: [Failed]
*** Failed! Falsifiable (after 4 tests):
[-1]
(used seed 4962194017620629261)

         Properties  Test Cases  Total
 Passed  0           1           1
 Failed  1           0           1
 Total   1           1           2
Test suite cooking: FAIL
Test suite logged to: dist/test/pasta-0.1.0.0-cooking.log
0 of 1 test suites (0 of 1 test cases) passed.
‚ûú

why? Of course, we’re assuming that all calory counts passed into the function will be positive!

So we have two ways to solve this:

Spoiler: once this is fixed, it fails again when the calory counts are insanely high, because then Int doubles back upon itself. You get the idea: the random approach, in sufficient numbers, will cover cases you wouldn’t normally have thought of yourself.

I will neatly sidestep the rabbit hole that is making generators for your own types for now (QuickCheck comes with a set of data generators for all the basic Haskell data types).

QuickCheck and HUnit tests can be grouped in one file without any problems in test-framework. (FWIW our carb-loaded example is on github)

Haddock

Haskell docs, of course. Again, not unlike java, it allows you to document library from annotated Haskell source code. You can annotate functions, various type declarations (data, newtype, type), typeclasses, modules.

Basically you annotate just above the entity you wish to describe, and you use comment (single-line — or multi-line {–) followed by the pipe symbol, or if you’re documenting in-line — followed by ^.

1
2
3
4
5
data T a b
  -- | This is the documentation for the 'C1' constructor
  = C1 a b
  -- | This is the documentation for the 'C2' constructor
  | C2 a b
1
2
3
f  :: Int      -- ^ The 'Int' argument
   -> Float    -- ^ The 'Float' argument
   -> IO ()    -- ^ The return value

(examples from the haddock documentation)

Run it

Tests are great, even better is to shorten the feedback loop by running them at every little change. For now I’m using a ruby solution to do that: using the ruby gem guard. guard will run cabal build and cabal test every time one of the source or test files change.

I found bindings for libevent in Haskell, so in theory there is nothing preventing such a tool to be written in Haskell. For all I know it exists, I just haven’t found it yet.

Edit: for OS X there is the tool hobbes. Would be interesting to make it cross-platform (thanks to Nick Partridge for the pointer).

That’s it for now. Comments welcome, as always.

Comments