2
votes

I am learning some Haskell, and I am trying to get my head around how pattern matching works. In doing so, I have written a simple nth function.

nth' :: Integer -> [a] -> a
nth' n [] = error "Index out of bound"
nth' n (x:xs) = if n == 0 then x else nth' (n - 1) xs

This first implementation seem to work as expected.

-- nth' 25 ['a'..'z']
-- 'z'
-- nth' 26 ['a'..'z']
-- *** Exception: Index out of bound

However, when I refactor it replacing the if statement with pattern matching, I end up getting the "Index out of bound" exception where I clearly should not.

nth' :: Integer -> [a] -> a
nth' _ [] = error "Index out of bound"
nth' 0 (x:[]) = x
nth' n (_:xs) = nth' (n - 1) xs

-- nth' 2 ['a'..'z']
-- *** Exception: Index out of bound

What am I doing wrong?

3

3 Answers

13
votes

The pattern x:[] matches a list that contains exactly one element. So your nth' 0 (x:[]) case will be executed only if the first argument is 0 and the second argument is a one-element list. If the second argument is a list with more than one argument, it'll go into the last case.

You need to change the second case to nth' 0 (x:_) = x, so that it matches no matter how many elements are in the list (as long as it's at least one).

6
votes

The problematic clause:

nth' 0 (x:[]) = x

It matches only one-element list. Try to change it with:

nth' 0 (x:_) = x
1
votes

Try:

    nth' :: Integer -> [a] -> a
    nth' _ [] = error "Index out of bound"
    -- nth' 0 (x:[]) = x -- matches one element list x:[] = [x]
    nth' 0 (x:_) = x     -- matches first element and everything (not just empty list)
    nth' n (_:xs) = nth' (n - 1) xs