Reputation: 23
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
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