Koschi13
Koschi13

Reputation: 619

Pattern matching list is failing

I have the following Code:

{-
Returns a list of Strings splitted by ' '. '\\ ' is kept as ' '
-}
splitOnWhitespace :: String -> [String]
splitOnWhitespace s = concatOnBackslash $ splitOn " " s

{-
concats Strings on '\\' with a whitespace
-}
270 concatOnBackslash :: [String] -> [String]
271 concatOnBackslash [] = []
273 concatOnBackslash [x] = [x]
274 concatOnBackslash [x, xs] = case last x of
275     '\\' -> [(init x) ++ " " ++ xs]
276     _ -> [x, xs]
277 concatBackslash (x : xx : xs) = case last x of
278     '\\' -> concatOnBackslash (((init x) ++ " " ++ xx) : xs)
279     _ -> x: concatOnBackslash (xx : xs)

Which compiles and should return me a list of Strings splitted at a non escaped white-space.

However the following happens:

*Filesystem> splitOnWhitespace "a1"
["a1"]
*Filesystem> splitOnWhitespace "a1 a2"
["a1","a2"]
*Filesystem> splitOnWhitespace "a1 a2 a3"
*** Exception: src/Filesystem.hs:(271,1)-(275,24): Non-exhaustive patterns in function concatOnBackslash

*Filesystem> splitOnWhitespace "a1 a2 a3 a4"
*** Exception: src/Filesystem.hs:(271,1)-(275,24): Non-exhaustive patterns in function concatOnBackslash

What am I doing wrong here?

Upvotes: 1

Views: 55

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477230

This is indeed correct. The first problem is that your last clause defines a function concatBackslash, whereas the other clauses define a function concatOnBackslash (with On). This thus means that Haskell considers this two different functions. You can rename these to:

concatOnBackslash :: [String] -> [String]
concatOnBackslash [] = []
concatOnBackslash [x] = [x]
concatOnBackslash [x, xs] = case last x of
    '\\' -> [(init x) ++ " " ++ xs]
    _ -> [x, xs]
concatOnBackslash (x : xx : xs) = case last x of
    '\\' -> concatOnBackslash (((init x) ++ " " ++ xx) : xs)
    _ -> x: concatOnBackslash (xx : xs)

Now the test case will work, but the function is still not very safe.

The last :: [a] -> a (and init :: [a] -> [a]) functions are non-total. For an empty list, these fail:

Prelude> last []
*** Exception: Prelude.last: empty list
Prelude> init []
*** Exception: Prelude.init: empty list

This thus means that concatOnBackslash thus will error, given the first element is empty, like:

Prelude Data.List.Split> concatOnBackslash ["", "a"]
*** Exception: Prelude.last: empty list

You thus better use pattern matching here, like:

concatOnBackslash :: [String] -> [String]
concatOnBackslash [] = []
concatOnBackslash [x] = [x]
concatOnBackslash [[], xs] = ...
concatOnBackslash [x@(_:_), xs] = case last x of
    '\\' -> [(init x) ++ " " ++ xs]
    _ -> [x, xs]
concatOnBackslash ([] : xx : xs) = ...
concatOnBackslash (x@(_:_) : xx : xs) = case last x of
    '\\' -> concatOnBackslash (((init x) ++ " " ++ xx) : xs)
    _ -> x: concatOnBackslash (xx : xs)

Upvotes: 2

Related Questions