MC3D
MC3D

Reputation: 23

Beginner Haskell problem - Couldn't match type ‘Bool’ with ‘[Char]’

I need to make a palindrome checker using recursion in Haskell for a homework assignment. The function should take in a string and return a Bool. When attempting to compile, I get the error, "Couldn't match type ‘Bool’ with ‘[Char]’."

I'm very new to Haskell, so I'm sure I'm just overlooking something silly, but I figured I'd ask for some help anyway. I've pasted my code below, as well as the full error I'm receiving.

isPalindrome :: String -> Bool
isPalindrome s
  | isPalindrome ((null s) || (length s == 1)) = True
  | isPalindrome ((s !! 0) /= (s !! (length s - 1))) = False
  | otherwise = isPalindrome (tail (init s))

My implementation checks to see if the input string is either empty or of size one, and returns true if it is. If it is not, it checks to see if the first character and last character are different, and if they are, returns false. Otherwise, it calls itself again, passing in the string with the first and last characters cut off.

main.hs:15:19: error:
    • Couldn't match type ‘Bool’ with ‘[Char]’
      Expected type: String
        Actual type: Bool
    • In the first argument of ‘isPalindrome’, namely
        ‘((null s) || (length s == 1))’
      In the expression: isPalindrome ((null s) || (length s == 1))
      In a stmt of a pattern guard for
                     an equation for ‘isPalindrome’:
        isPalindrome ((null s) || (length s == 1))
   |
15 |   | isPalindrome ((null s) || (length s == 1)) = True
   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
main.hs:16:19: error:
    • Couldn't match type ‘Bool’ with ‘[Char]’
      Expected type: String
        Actual type: Bool
    • In the first argument of ‘isPalindrome’, namely
        ‘((s !! 0) /= (s !! (length s - 1)))’
      In the expression: isPalindrome ((s !! 0) /= (s !! (length s - 1)))
      In a stmt of a pattern guard for
                     an equation for ‘isPalindrome’:
        isPalindrome ((s !! 0) /= (s !! (length s - 1)))
   |
16 |   | isPalindrome ((s !! 0) /= (s !! (length s - 1))) = False
   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Upvotes: 2

Views: 687

Answers (1)

Will Ness
Will Ness

Reputation: 71065

f x is a function call to the function f with the argument x. But you don't need to call that function, if the test expression x is already all that you need:

isPalindrome :: String -> Bool
isPalindrome s
  --   f               x
  | {- isPalindrome -} ((null s) || (length s == 1))        -- here
              = True
  | {- isPalindrome -} ((s !! 0) /= (s !! (length s - 1)))  -- and here
              = False
  | otherwise = isPalindrome (tail (init s))  

isPalindrome :: String -> Bool means isPalindrom's first argument is expected to be a String. But what is really meant to be there is a Boolean value, used as a guard, to select the appropriate course of action. Hence the error message. We already have that Bool.

The function call in the last line is the recursive call that indeed must be done.

{- ... -} are multiline comments, in Haskell.


The usual, more idiomatic Haskell way is to not perform those tests explicitly, but rather arrange for the equivalent pattern matching in the function definition by clauses:

isPalindrome :: String -> Bool
isPalindrome []     = True           -- (null s)
isPalindrome [_]    = True           -- (length s == 1)
isPalindrome -- [a, ..., b] | a /= b
             (x:xs) | x /= last xs
                    = False          --  ((s !! 0) /= (s !! (length s - 1))) 
isPalindrome -- [a, ...xs, b] 
             (_:xs) = isPalindrome   --  xs
                                   (init xs)

The above code contains some imaginary list patterns in the comments, and their Haskell equivalents in the code itself.

In fact, as @chepner points out in the comments, patterns can often help avoid inefficiency: calculating length is O(n) whereas the pattern matching with [_] is O(1).

Of course this specific code is very inefficient still as the other two clauses also perform O(n) operations (last and init). An O(n) algorithm exists.

Upvotes: 5

Related Questions