What to return if a list is empty

lastButOne xs = if null xs 
    then  0
    else xs !! ((length xs) - 2)

I am reading Chapter 2. Types and Functions . I am doing one of their challenge questions and was asked to get the second to last item in a list.

My solution was the code attached. I don’t know what to return if a list is empty. Should I throw an error. if the list I pass in is empty.

I also noticed I recieved an error message of

    • Non type-variable argument in the constraint: Num [a]
      (Use FlexibleContexts to permit this)
    • When checking the inferred type
        it :: forall a. Num [a] => [a]

When I returned 2 different types from the function. What does the error mean ?
I produced this solution with the code below.

lastButOne xs = if null xs 
    then  xs
    else xs !! ((length xs) - 2)
1 Like

Since you are mimicking last (which throws an exception), you should. (then error "list too short" will do.)

I cannot reproduce the error on my machine (I get an infinite type typecheck error); maybe you pasted an earlier revision?

1 Like

or you could use a Maybe :

lastButOne xs = if null xs 
    then  Nothing
    else Just ...

also, note that this only checks that the list is empty, not that it contains at least 3 elements.


Thank you for the reply! It was another condition I did not consider.

Thank you for for the help as this is my first time in Haskell.

Here is the solution I came up with .

lastButOne xs = if (null xs) || ((length xs) < 3)
    then  error "list is to short"
    else xs !! ((length xs) - 2)
lastButOne xs = if (length xs) < 3
    then  error "list is to short"
    else xs !! ((length xs) - 2)

will work equally well. null xs isn’t some special thing you need to check first.

Also, 3 is overkill, (length xs) < 2 is the correct condition because length xs = 2 means xs !! 0 is what you end up, with is fine. A 2-long list is not, in fact, too short.

It’s really easy to do these off-by-one errors with hard-coded numbers like your 2…so it’s better to not write numbers at all! I would instead reverse the list and then use plain pattern matching. See if you can complete this:

lastButOne xs = case reverse xs of
    -- pattern -> error "list is to short"
    -- pattern binding `x` -> x
1 Like

Hey thank you so much for the challenge.
Here is what I came up with.

listButOne :: [a] -> a
listButOne xs = case reverse xs of 
    [] -> error "this list is empty"
    (a:y:[]) -> a
    (x:y:_) -> y

OK, getting close! I would make sure you have -Wall on. you will then see you have some unmatched patterns. I would also write a few tests (don’t bother with any fancy test suite library, just write down some (expected, result) pairs to poke at in GHCi).

Thank you again for the added challenges. I don’t understand what you mean by -Wall. Is this some sort of catch all ? Can I find this in some sort of documentation ?