Functional Programming Unit Testing – Part 7 Thursday, January 29, 2009

In the previous installment in this series, I covered how you can use type classes to implement operators to allow you to do such things as approximate equals for floating point calculations, so that you could run QuickCheck property tests with relative ease. This post will get us back on track to our refactoring tales and what tools we can use to better understand the language.

Let’s get caught up to where we are today:

Improving Your Codebase

One of the big questions that invariably comes up when introducing yourself to a new language is “How can I tell if I’m using it correctly?” When we explore new languages, we always look to have some sort of hand holding to determine if there is a better way of accomplishing the task at hand. There are the more traditional avenues such as the #haskell IRC channel, the Haskell-Cafe mailing list and Hoogle, but one tool that stands out is HLint.

HLint, created by Neil Mitchell, is a tool that can provide suggestions for common mistakes made in the language. This, deriving its name from the lint tool, can help refine your codebase and better utilize the features of the language. Tools such as this really allow you to become a bit more fluent in the language. As we approach each language that we learn, it’s important that we understand not only the basics, but the many nuances, just as you would with a spoken language.

What are exactly its capabilities? Let’s cover some of the basic suggestions it can give, then walk through several examples.

List recursion hints

Map/Fold (Left & Right) hints

Unnecessary parentheses and brackets

Eta reductions

Monadic sugar hints

And more

Let’s first look through some trivial hints such as functional composition:

mapFilterReduce xs =

sum (filter (\x -> x `mod` 3 == 0) (map (flip (^) 3) xs))

We can then run this through the HLint package to see if the tool has any relevant suggestions:

>hlint "hlintexample.hs"

hlintexample.hs:1:1: Eta reduce

Found:

mapFilterReduce xs

= sum (filter (\ x -> x `mod` 3 == 0) (map (flip (^) 3) xs))

Why not:

mapFilterReduce

= sum . filter (\ x -> x `mod` 3 == 0) . map (flip (^) 3)



Found 1 suggestions

This gives us a pretty nice suggestion which is to use the dot-operator in order to compose the functions together. This also then lets us omit the xs as a function argument as it now will be inferred usage.

Another example is to do another simple calculation. Let’s take the following code example which calculates mean values on odd numbered lists:

import Data.List (sort)



meanOdd xs =

head $ drop (length xs `div` 2) (sort xs)

Running this through HLint will give us the following results:

>hlint "hlintexample.hs"

hlintexample.hs:3:1: Use index

Found:

head $ drop (length xs `div` 2) (sort xs)

Why not:

sort xs !! (length xs `div` 2)



Found 1 suggestions

What this tells us is that instead of the head and drop functions, we could use the !! list index operator to retrieve the value from the list. This is looking pretty good so far. What Neil has done that is interesting is running this tool against the base libraries in the GHC compiler to find some opportunities as well. What I’ve shown here barely scratches the surface with some of the possibilities here.

The opportunity for something like this in the F# world is huge. Not only refactoring tools that allow for such things as removal of parameters, reorder parameters, promote lambda to named function, etc, but to actually tell you which functions you might be better off calling instead. I hope the open source community, the refactoring tools vendors and others are looking at this opportunity.

What Have We Learned?

I hope this series was helpful to those who are looking at design and testing in the functional programming world. After 8 posts, I’ve covered quite a bit in the functional programming world from traditional xUnit testing, to QuickCheck property-based tests, to refactoring and other interesting areas. It’s great to see others such as Joe Armstrong talking about “Micro Lightweight Unit Testing” in Erlang as well.

There are some additional resources in the form of videos that might interest you on QuickCheck.

These videos are great to explain a little further some of the techniques of how you testing using QuickCheck in a little deeper discussion than I have provided here. This has been a fun series to put together and I hope it draws a picture of the design and test options we have in the functional programming world.