Posted on Thursday, 9th September 2010 by charpi

My post don’t pretend to be a complete tutorial on Haskell but just a sum up of my learning experience. My main objective is to understand this language, to see its strengths and its weakness.

To learn a new language, I always found funny to try to create a simple unittest framework.

Writing such framework can be tedious or very easy (thanks erlang), so such experiment give me a good overview of the language and how easy I can learn it. I’ve decided to do the same exercise with Haskell.

Goal: Write a function which counts the number of occurrence of a character in a string

Let’s start to write a module with one test case.

module CharpiLib_test where
import CharpiLib

test_empty_string = (0 == occurence 'x' "")

and the corresponding dumb implementation

module CharpiLib where

occurence :: Char -> String -> Integer
occurence _ _ = 0

This code compile and can be executed

charpi@home:~/haskell/unittest$ ghci 
GHCi, version 6.10.4: http://www.haskell.org/ghc/  :?  for help
Loading package ghc-prim ... linking ... done.
Loading package integer ... linking ... done.
Loading package base ... linking ... done.
Prelude> :load CharpiLib_test.hs
[1 of 2] Compiling CharpiLib        ( CharpiLib.hs, interpreted )
[2 of 2] Compiling CharpiLib_test   ( CharpiLib_test.hs, interpreted )
Ok, modules loaded: CharpiLib_test, CharpiLib.
*CharpiLib_test> test_empty_string
True
*CharpiLib_test> 

For the exercise, I add several test cases in a single step

module CharpiLib_test where
import CharpiLib

test_empty_string =  (0 == (occurence 'x' ""))

test_char_not_found = (0 == (occurence 'x' "hello world"))

test_one_occurence = (1 == (occurence 'h' "hello world"))

test_sevaral_occurence = (3 == (occurence 'l' "hello world"))

and execute each tests

*CharpiLib_test> :load CharpiLib_test.hs
[1 of 2] Compiling CharpiLib        ( CharpiLib.hs, interpreted )
[2 of 2] Compiling CharpiLib_test   ( CharpiLib_test.hs, interpreted )
Ok, modules loaded: CharpiLib_test, CharpiLib.
*CharpiLib_test> test_empty_string
True
*CharpiLib_test> test_one_occurence
False
*CharpiLib_test> test_sevaral_occurence
False
*CharpiLib_test> 

I can execute each test case one by one but I need something to run all the test functions in one phase.
Let’s add a function returning the list of all my test cases

suite = [test_empty_string,
         test_char_not_found,
         test_one_occurence,
         test_sevaral_occurence]
*CharpiLib_test> :load CharpiLib_test.hs
[1 of 2] Compiling CharpiLib        ( CharpiLib.hs, interpreted )
[2 of 2] Compiling CharpiLib_test   ( CharpiLib_test.hs, interpreted )
Ok, modules loaded: CharpiLib_test, CharpiLib.
*CharpiLib_test> suite
[True,True,False,False]
*CharpiLib_test> 

When I evaluate the function suite, each function of the list are evaluated and I got a final result. The example isn’t very clear but I guess that some Lazy evaluations have been made.
Lazy evaluation will be detailed in a next post. As Haskell and Erlang don’t evaluate functions in the same way, you really need to understand this notion if you want to use it efficiently.

Let’s finish my very simple unit test framework by adding a description to the test cases and by finishing on an exception if an error occurs.

module CharpiLib_test where
import CharpiLib

test_empty_string =  ("Test empty string",
                      (0 == (occurence 'x' "")))

test_char_not_found = ("No character found",
                       (0 == (occurence 'x' "hello world")))

test_one_occurence = ("One character found",
                      (1 == (occurence 'h' "hello world")))

test_sevaral_occurence = ("More than one character found",
                          (3 == (occurence 'l' "hello world")))

suite = [test_empty_string,
         test_char_not_found,
         test_one_occurence,
         test_sevaral_occurence]

run =  [ assert t | t <- suite]

assert (_,True) = True
assert (description, False) = error (description ++ ": Assertion failed")
*CharpiLib_test> :load CharpiLib_test.hs
[1 of 2] Compiling CharpiLib        ( CharpiLib.hs, interpreted )
[2 of 2] Compiling CharpiLib_test   ( CharpiLib_test.hs, interpreted )
Ok, modules loaded: CharpiLib_test, CharpiLib.
*CharpiLib_test> run
[True,True,*** Exception: One character found: Assertion failed
*CharpiLib_test> 

and now the implementation

module CharpiLib where

occurence char string =  do_occurence char string 0

do_occurence _ [] acc = acc
do_occurence c (x:xs) acc
     | c == x  = do_occurence c xs (acc+1)
     | c /= x = do_occurence c xs acc
*CharpiLib_test> :load CharpiLib_test.hs
[1 of 2] Compiling CharpiLib        ( CharpiLib.hs, interpreted )
[2 of 2] Compiling CharpiLib_test   ( CharpiLib_test.hs, interpreted )
Ok, modules loaded: CharpiLib_test, CharpiLib.
*CharpiLib_test> run
[True,True,True,True]
*CharpiLib_test> 

Done :-)

Conclusion: I need more time to learn haskell and how to use it efficiently.

  • Type errors are still very obscure for me.
  • Monads is the last frontier

Tags: ,
Posted in Uncategorized | Comments (3,285)

Comments are closed.