AstroEngiSci
AstroEngiSci

Reputation: 23

Why is this Haskell function inferred to this type?

I'm a Haskell beginner working through an exercise in Chapter 2 of Real World Haskell, where you write a function that returns the second-to-last element of a list.

I've written the following function to attempt to solve this:

-- file: \Learn Haskell\lastButOne.hs
lastButOne xs = if length xs == 2
                then head xs
                else lastButOne tail xs

What I don't understand is this error the compiler throws:

lastButOne.hs:2:1: error:
    • Couldn't match type ‘[a] -> [a]’ with ‘[t]’
      Expected type: ([a] -> [a]) -> [t] -> t
        Actual type: [t] -> t
    • Relevant bindings include
        lastButOne :: ([a] -> [a]) -> [t] -> t (bound at lastButOne.hs:2:1)

From what I understand, ghci thinks my function ought to have a different type than it does, but I don't understand why that's happening, or how I can fix it.

Edit: Thanks for the answers! I've updated my code to:

-- file: \Learn Haskell\lastButOne.hs
lastButOne xs = if length xs == 2
                then head xs
                else if length xs < 2
                     then error "This list does not have a second-to-last element."
                     else lastButOne (tail xs)

This eliminates the error where tail xs is being interpreted as two arguments, rather than a single expression. I also added some code that ensures the list isn't too short. Willem Van Onsem's solution is nicer, but as an exercise I thought I'd come up with a solution that only used the concepts introduced so far in the book.

Upvotes: 2

Views: 133

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477533

The problem is with the line:

        else lastButOne tail xs

It should be:

        else lastButOne (tail xs)

Otherwise ghci things that you give lastButOne two arguments: tail and xs.

You can however make the code more elegant:

lastButOne [x,_] = x
lastButOne xs = lastButOne $ tail xs

Furthermore you will have to find a way to resolve lists with one element, and the empty list. Right now the function will start looping. An idea could be to to error on these lists, like:

lastButOne [x,_] = x
lastButOne (_:xs) = lastButOne xs
lastButOne [] = error "The list has no last but one element."

Upvotes: 3

Related Questions