user12530264
user12530264

Reputation:

Why is one expression valid while the other is not?

My definition starts this way:

isPalindrome' :: (Eq a) => [a] -> Bool
isPalindrome' [] = True
isPalindrome' [_] = True

Adding one more line to this definition, one approach throws errors while the other works fine.

Works:

isPalindrome' xs = head xs == last xs && (isPalindrome' $ init $ tail xs)

isPalindrome' xs = head xs == last xs && isPalindrome' ( init $ tail xs)

Fails:

isPalindrome' xs = head xs == last xs && isPalindrome' $ init $ tail xs

I don't understand why this fails.

Doesn't isPalindrome' $ init $ tail xs mean the same thing as isPalindrome' ( init ( tail xs))?

Upvotes: 0

Views: 61

Answers (1)

Will Ness
Will Ness

Reputation: 71065

Your failing expression

isPalindrome' xs = head xs == last xs && isPalindrome' $ init $ tail xs

is actually parsed / iterpreted / by Haskell as

isPalindrome' xs = ((head xs == last xs) && isPalindrome') (init (tail xs))

and this obviously isn't good.

How can we tell that it is parsed that way? Using the :i command we discover

> :i &&
infixr 3 &&

> :i ==
infix 4 ==

> :i $
infixr 0 $

=='s precedence is 4 so it is higher than &&'s (which is 3), so it binds its operands first, and thus (head xs == last xs) expression is formed. Then, &&'s precedence 3 is higher than $'s 0, so && binds its operands and thus ((head xs == last xs) && isPalindrome') expression is formed. Then $ gets to work, which does associate on the right.

Another way to see this is through error messages (which you should always include in questions):

> isPalindrome' xs = head xs == last xs && isPalindrome' $ init $ tail xs

<interactive>:184:24:
    Couldn't match expected type `[a] -> t' with actual type `Bool'
    Relevant bindings include 
         ..............
         ..............

<interactive>:184:46:
    Couldn't match expected type `Bool' with actual type `[a] -> t'
    Relevant bindings include
      xs :: [a] (bound at <interactive>:184:19)
      isPalindrome' :: [a] -> t (bound at <interactive>:184:5)
    Probable cause: isPalindrome' is applied to too few arguments
    In the second argument of `(&&)', namely isPalindrome'
          ^^^^^^^^^^^^^^^^^^^^^^^^^
    In the expression: head xs == last xs && isPalindrome'
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

So we can see here how it was parsed, and that && was interpreted as the top operator in this expression.

The reason isPalindrome' xs = head xs == last xs && isPalindrome' ( init $ tail xs) works is that the "space" after isPalindrome' i.e. the application by juxtaposition has the highest possible "precedence" in Haskell.

Upvotes: 2

Related Questions