Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid “Exception: Prelude.head: empty list”? - Haskell

Tags:

list

haskell

Good evening everyone, I'm new to haskell. I'm trying to sum up a list of reading a string Unicode values and store them in a list and then sum the integers up.

 getLetterUnicodeValue :: Char -> Int
 getLetterUnicodeValue l = (ord l) - 64

 unicodeValueList :: String -> [Int]
 unicodeValueList x = getLetterUnicodeValue (head x) : unicodeValueList (tail x)

 total :: [Int] -> Int
 total []     = 0
 total x = (head x) + (total (tail x))

I got the error of empty list when the string come to the last character and the sum up function cannot successfully execute. Is there any way to stop the function unicodeValueList when it comes to its end.

*** Exception: Prelude.head: empty list
like image 685
Tri Nguyen Avatar asked Oct 04 '16 23:10

Tri Nguyen


2 Answers

The surest way to avoid this exception is not to use head. Instead you can use pattern matching to get the head and tail of a list:

unicodeValueList (x:xs) = getLetterUnicodeValue x : unicodeValueList xs

total (x:xs) = x + total xs

This way x and xs will only be available when the list is non-empty and it is guaranteed that you never accidentally access the head or tail of an empty list.

Of course now you'll get a warning that the pattern match is incomplete: You don't specify what should happen when the list is empty. Of course this was true before as well, but now that you use pattern matching the compiler can actually see this and warn you about it (whereas the previous code crashed at runtime without any prior warning).

So what should be done when the list is empty? Well, an empty string contains no unicode values, right? So it should return the empty list when the input is empty:

unicodeValueList [] = []

Of course you don't need pattern matching to fix your error. You could just use an if to make sure that you only call head and tail when the list is not empty. But if you do that, the compiler won't be able to verify that your checks are in order. If you use pattern matching and avoid the unsafe head and tail functions completely, you'll never be able to accidentally access the head or tail of an empty list and the compiler will warn you if you ever forget to consider that the list might be empty.

like image 134
sepp2k Avatar answered Nov 13 '22 15:11

sepp2k


Yep, you just will have to pattern match in unicodeValueList

unicodeValueList :: String -> [Int]
unicodeValueList [] = []
unicodeValueList (x:xs) = getLetterUnicodeValue x : unicodeValueList xs

Note this could be written more nicely as unicodeValueList = map getLetterUnicodeValue. The reason you are getting the error for head is that your recursion had no base case - it keeps trying to recurse, even when it has reached the empty list.

like image 23
Alec Avatar answered Nov 13 '22 15:11

Alec