[Haskell-cafe] Type System vs Test Driven Development

Heinrich Apfelmus apfelmus at quantentunnel.de
Sat Jan 8 14:33:54 CET 2011


Florian Weimer wrote:
> * Jonathan Geddes:
> 
>> When I write Haskell code, I write functions (and monadic actions)
>> that are either a) so trivial that writing any kind of unit/property
>> test seems silly, or are b) composed of other trivial functions using
>> equally-trivial combinators.
> 
> You can write in this style in any language which has good support for
> functional composition (which means some sort of garbage collection
> and perhaps closures, but strong support for higher-order functions is
> probably not so important).  But this doesn't mean that you don't have
> bugs.  There are a few error patterns I've seen when following this
> style (albeit not in Haskell):

As you mention, the bugs below can all be avoided in Haskell by using 
the type system and the right abstractions and combinators. You can't 
put everything in the type system - at some point, you do have to write 
actual code - but you can isolate potential bugs to the point that their 
correctness becomes obvious.

> While traversing a data structure (or parsing some input), you fail to
> make progress and end up in an infinite loop.

Remedy: favor higher-order combinators like  fold  and  map  over 
primitive recursion.

> You confuse right and left (or swap two parameters of the same type),
> leading to wrong results.

Remedy: use more descriptive types, for instance by putting  Int  into 
an appropriate newtype. Use infix notation  source `link` target.

> All the usual stuff about border conditions still applies.

Partial remedy: choose natural boundary conditions, for example  and [] 
= True,  or [] = False.

But I would agree that this is one of the main use cases for QuickCheck.

> Input and output is more often untyped than typed.  Typos in magic
> string constants (such as SQL statements) happen frequently.

Remedy: write magic string only once. Put them in a type-safe combinator 
library.

> Therefore, I think that you cannot really avoid extensive testing for
> a large class of programming tasks.

Hopefully, the class is not so large anymore. ;)

>> I vehemently disagreed, stating that invariants embedded in the type
>> system are stronger than any other form of assuring correctness I know
>> of.
> 
> But there are very interesting invariants you cannot easily express in
> the type system, such as "this list is of finite length".

Sure, you can.

    data FiniteList   a = Nil | Cons a !(FiniteList a)

I never needed to know whether a list is finite, though. It is more 
interesting to know whether a list is infinite.

    data InfiniteList a = a :> InfiniteList a

> It also
> seems to me that most Haskell programmers do not bother to turn the
> typechecker into some sort of proof checker.  (Just pick a few
> standard data structures on hackage and see if they perform such
> encoding. 8-)

I at least regularly encode properties in the types, even if it's only a 
type synonym. I also try to avoid classes of bugs by "making them 
obvious", i.e. organizing my code in such a way that correctness becomes 
obvious.


Regards,
Heinrich Apfelmus

--
http://apfelmus.nfshost.com




More information about the Haskell-Cafe mailing list