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: erlang, haskell
Posted in Uncategorized | Comments (3,285)