Errors 101

Everyone makes mistakes all the time, and this is especially true for coding. Being able to understand error messages is one of the most important aspects of writing code; how can you hope to fix your program if you don’t know whats wrong with it?

Reading error messages

Example 1

Let’s start off simple. Assume that the following is an example of a function that outputs the second element of a list:

secondElem :: [a] -> a
secondElem list = case list of
    [] -> error " list is too short"
    x:y:xs -> y

No error would occur in compiling this code. However, if we run secondElem [1] we would get the following error message:

*** Exception: Comp1100.hs:(2,19)-(4,15): Non-exhaustive patterns in case

This is simply saying that there is at least one “case” for which the pattern matching of the function has not been defined. Note that here Comp1100.hs refers to the name of the haskell file, (2,19)-(4,15) refers to the range in which this error occurs, that is from the 19th character of the second line, to the 15th chartacter of the 4th line. In short haskell tends to use the structure (line number,character number) to represent these locations.

Example 2

Finding the maximum of two numbers with the use of guards:

maxWithGuards  :: a -> a -> a
maxWithGuards  a b
    | (a > b) = a
    | otherwise  = b

When compiling the above code we get the following error:

Comp1100.hs:19:8: error:
  * No instance for (Ord a) arising from a use of `>'
    Possible fix:
      add (Ord a) to the context of
        the type signature for:
          maxWithGuards :: forall a. a -> a -> a
  * In the expression: (a > b)
    In a stmt of a pattern guard for
                    an equation for `maxWithGuards':
      (a > b)
    In an equation for `maxWithGuards':
        maxWithGuards a b
          | (a > b) = a
          | otherwise = b
  |
19 |     | (a > b) = a
  |        ^^^^^

Lets analyse this line by line: Comp1100.hs:19:8 denotes the location of the error. In other words this means in the 8th character of the 19th line in the haskell file titled Comp1100.hs. No instance for (Ord a) arising from a use of `>' This indicates that the use of “>” symbol requires the Ord character, and there is no instance of ord defined for the varibale provided. Notice the type signature of the function to be maxWithGuards :: a -> a -> a In the error message we see the line: maxWithGuards :: forall a. a -> a -> a This is haskell telling you that the type signature has been defined for all types of a, and hence it should run for code such as maxWithGuards True False but it wouldn’t be able to evaluate the answer to True > False as it clearly makes no sense. Haskell has also included a ‘possible fix’ and has mentioned that we should add (Ord a) to the context of the type signature which is simply highlighting the need of the (Ord a) to specify a smaller set of types which are allowed to be used for the function.

maxWithGuards  :: (Ord a) => a -> a -> a
maxWithGuards  a b
    | (a > b) = a
    | otherwise = b

Example 3

The following example is another along the same lines as the previous example except this time instead of considering errors in the type signature, lets look into errors in data declaration. Lets assume you wanted to define a data constructor called Options that was either Yes or No. If we attempt to define this without capitalisation we will see this:

 Not a data constructor: `yes'
  |
3 | data Options = yes | no
  |                ^^^

Hence, all data type definitions must begin with a capital letter.

data Options = Yes | No

Further, if we were to run the following code:

oneZero :: Int -> Options
oneZero n
    | 1 = yes
    | 0 = no
    | otherwise = error

We would see this error:

Comp1100.hs:7:11: error:
    * Variable not in scope: yes :: Options
    * Perhaps you meant data constructor `Yes' (line 3)
  |
7 |     | 1 = yes
  |           ^^^

Comp1100.hs:8:11: error:
    * Variable not in scope: no :: Options
    * Perhaps you meant one of these:
        `n' (line 6), data constructor `No' (line 3),
        `not' (imported from Prelude)
  |
8 |     | 0 = no
  |           ^^
Failed, no modules loaded.

Crickey!

This error message is stating the fact that the outputs yes and no have been mentioned without a capital, so haskell indentifies them as variables instead of data constructors. Hence, in order for them to be identified as data constructors we must use “Yes” and “No” whenever we want to reference the data type.

Example 4

Moving on to another common error, have Look at the following code and try to see if you can spot the error:

head1 :: [a] -> a
head1 list = case list of
    [] -> error
    x:_ -> x

Tricky isn’t it? So when we compile the code, haskell throws the following error message at you:

Comp1100.hs:63:11: error:
    * Couldn't match expected type `a' with actual type `[Char] -> a0'
      `a' is a rigid type variable bound by
        the type signature for:
          head1 :: forall a. [a] -> a
        at Comp1100.hs:61:1-17
    * In the expression: error
      In a case alternative: [] -> error
      In the expression:
        case list of
          [] -> error
          x : _ -> x
    * Relevant bindings include
        list :: [a] (bound at Comp1100.hs:62:7)
        head1 :: [a] -> a (bound at Comp1100.hs:62:1)
   |
63 |     [] -> error
   |           ^^^^^

So what does this message tell you? As discussed earlie the first part shows the location of the error (11th character of the 63rd line), but the rest is a bit cryptic. Couldn't match expected type `a' with actual type `[Char] -> a0' This indicates that the problem here is the fact that error is of type [Char] -> a0, but we require type a0.

Just like everything else in haskell, error is a function too. error is a predefined function that takes a string and generates a error message, so in order to get an error message we need to provide a string to the error function. By simply altering the code to the following, we will no longer receive an error:

head1 :: [a] -> a
head1 list = case list of
    [] -> error "empty list!"
    x:_ -> x

All of that huge error message just to say you’ve forgotten one string!

Example 5

The following is another important error to understand.

Comp1100.hs:65:1: error:
    parse error (possibly incorrect indentation or mismatched brackets)
   |
65 | addInt :: Int -> Int -> Int
   | ^
Failed, no modules loaded.

Generally, parse error refers to either incorrect indentation or mismatched brackets in your code. In addition to mentioning this haskell gives you another clue inorder to identify this error:

   |
65 | addInt :: Int -> Int -> Int
   | ^

The arrow pointing to the first character of the line indicates that the error is actually somewhere above it in the code (in this case the error should be in the function defined prior to line 65).

Debugging

Now that you have some idea on how to read errors, it is your turn to try it out for yourself. For the following code, it is your job to determine if there is a problem with the code, and if there is, what kind of error message would ghci return? Feel free to use your newfound debugging skill to fix the functions that are broken!

divide :: (Num a) => a -> a -> a
divide x y = x / y
filterLists :: [[a]] -> ([a] -> Bool) -> [[a]]
filterLists lst fn = case (fn (head lst)) of
    True -> [head lst] ++ filterLists(tail lst)
    False -> filterLists (tail lst)
magicNumber :: (Num a) => a -> [Char]
magicNumber num
    | num < 25 || num > 60 = "Not even close"
    | (num > 25 && num < 35) || (num < 60 && num > 48) = "Getting warmer"
    | (num > 35 && num < 41) || (num < 48 && num > 43) = "Very close"
    | num == 42 = "You got it!"
listOfThree :: [a] -> Bool
listOfThree lst = case lst of
    [] -> False
    x:[] -> False
    x:y:[] ->False
    x:y:z -> True
data Apple = Green | Red
type Result = (Apple,Integer)

greenOrRed :: [Apple] -> (Result, Result) -> Result
greenOrRed apples ((_,x),(_,y)) = case apples of
    [] -> error "Empty list"
    a:[]
        | a == Red || (x+1) > y -> (Red, (x+1))
        | a == Red || (x+1) < y -> (Green, (y))
        | a == Green || (y+1) > x -> (Red, (x))
        | a == Green || (y+1) < x -> (Green, (y+1))
    a:as
        | a == Red -> greenOrRed as ((Red, (x+1)), (Green, (y)))
        | a == Green -> greenOrRed as ((Red, (x)), (Green, (y+1)))