Search code examples
listhaskellnon-exhaustive-patterns

Haskell - "Non-exhaustive patterns" error with a function using list


I'm trying to make a function in haskell to know if all the elements in a list of list have the same length. (I've search answers in previous posts but none of them works).

sameLength :: [[t]] -> String
sameLength [] = "Empty list"
sameLength [[items]]
    | and $ map (\x -> length x == (length $ head [[items]])) [[items]] = "Same length"
    | otherwise = "Not the same length"

The problem is that it doesn't work :

*Main> :l test.hs
[1 of 1] Compiling Main             ( test.hs, interpreted )
Ok, modules loaded: Main.
*Main> sameLength []
"Empty list"
*Main> sameLength [[1,2],[3,4]]
"*** Exception: test.hs:(2,1)-(5,39): Non-exhaustive patterns in function sameLength

*Main> sameLength [[1,2]]
"*** Exception: test.hs:(2,1)-(5,39): Non-exhaustive patterns in function sameLength

I don't really see where is the problem. It treat the case in which the parameter is an empty list and in which it is not. Am I wrong ? Did i miss something ?

Thanks for your help :)


Solution

  • you have too many [..] in here:

    sameLength [[items]] 
    

    (as Silvio explained really well) - try

    sameLength items 
    

    instead.

    Further as a == a, you don't have to check if the length of the head is the same as the length of the head` (of course) and so I would recommend doing something like this:

    sameLength :: [[a]] -> Bool
    sameLength []     = True
    sameLength (h:tl) = all ((length h ==) . length) tl
    

    as I think the Bool result is just more useful and natural

    how does this work?

    all takes a predicate and a list and checks if the predicate holds for each element of the list - so (length h ==) . length = \xs -> length h == length xs as a predicate checks if a given list xs has the same length as the head-list h - so due to the remark above you only have to check this with the tail-list tl

    remark

    You can argue if all elements of the empty list should have the same length - but I think the answer should be yes ;)

    examples

    Prelude> sameLength [[1,2],[3,4]]
    True
    Prelude> sameLength [[1,2],[3,4,5]]
    False
    Prelude> sameLength [[1,2]]
    True
    Prelude> sameLength []
    True
    

    in case you are concerned about performance

    (or you do not like the point-free style)

    sameLength :: [[a]] -> Bool
    sameLength []     = True
    sameLength (h:tl) = let l = length h
                        in all (\xs -> length xs == l) tl