1

I follow the real world haskell, and there is an exercise in chapter 2.

My solution is

lastButOne xs = if null xs || null (tail xs)
                then []
                else if null (tail (tail xs))
                     then head xs
                     else lastButOne (tail xs)

but it doesn't work other than [], and produces such an error.

*Main> lastButOne []
[]
*Main> lastButOne [1, 2]

<interactive>:5:13:
    No instance for (Num [a0]) arising from the literal `1'
    Possible fix: add an instance declaration for (Num [a0])
    In the expression: 1
    In the first argument of `lastButOne', namely `[1, 2]'
    In the expression: lastButOne [1, 2]

I am fairly a newbie and don't understand the cryptic error messages. Any ideas?

4
  • 2
    You'll find this much easier to do if you pattern match on the list instead of trying to deconstruct it using null, head and tail. Commented Jun 6, 2013 at 14:29
  • does [1,2] really have room for a tail of a tail? Commented Jun 6, 2013 at 14:29
  • Lee//Thank you for the suggestion, but I didn't see any mention about pattern matching in chapter 1 and chapter 2. So I don't know about it. Commented Jun 6, 2013 at 14:39
  • VoronoiPotato//I think tail (tail [1, 2]) is just [] Commented Jun 6, 2013 at 14:40

3 Answers 3

7

This is a type problem. If you use GHCi, load this function into it and use

:t lastButOne

to see its type, which is

lastButOne :: [[a]] -> [a]

this is because if need to have the same type on the then and else branches, and since you are returning a [] in the then branch, Haskell thinks you are trying to return a list always, and since you are returning head xs on the else branch, it thinks you are writing a function works on list of lists.

However, [1, 2] is not a list of lists, so GHC yelled at you about the type mismatch error.

Also note if you write out the type definition explicitly, it wouldn't compile:

lastButOne :: [a] -> a
lastButOne xs = if null xs || null (tail xs)
            then []
            else if null (tail (tail xs))
                 then head xs
                 else lastButOne (tail xs)

GHCi gets you an error:

Couldn't match type `a' with `[a0]'
  `a' is a rigid type variable bound by
      the type signature for lastButOne :: [a] -> a at k.hs:2:1
In the expression: []
In the expression:
  if null xs || null (tail xs) then
      []
  else
      if null (tail (tail xs)) then head xs else lastButOne (tail xs)
In an equation for `lastButOne':
    lastButOne xs
      = if null xs || null (tail xs) then
            []
        else
            if null (tail (tail xs)) then head xs else lastButOne (tail xs)
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you. This answer is just what I wanted. So, [] is interpreted as a list and that was the problem...
1

then [] here you return a list.

then head xs here you return what's in the list (a number in your case). I'm surprised this compiles at all. You should wrap the result with a Maybe, so the result of lastButOne [] and lastButOne [x] should be Nothing, and the result of lastButOne [...,x,_] should be Just x.

Or you could use the error pseudofunction in the error case.

1 Comment

I see. Following Ziyao's answer, the type of the lastButOne is [[a]]->[a], and it works well for such as lastButOne [[1,2], [1, 2, 3], [1, 2, 3, 4]]. I think, that's the reason why this is compiled successfully.
1

I think pattern matching is more elegant for this... Being myself a total newbie to Haskell (I just read a little bit about it long ago):

lastButOne ([]) = []
lastButOne (beforeLast:last:[]) = beforeLast
lastButOne (x:xs) = lastButOne xs

I know that it's not an explanation to your error, but sometimes the best solution is to avoid the problem at all!

2 Comments

This is definitely a cleaner solution. @Jong-BeomKim - it would be worth looking at how much nicer this is than all the ifs you have at the moment.
Thank you, I know the pattern matching now, but it is introduced in chapter 3 and the exercise is at the end of the chapter 2. It seems like the exercises and some examples in Real World Haskell demand more knowledge than that is already taught. As a completely beginner, I should find more organized tutorial...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.