tags:

views:

284

answers:

4

I am trying to teach myself Haskell. One thing I have tried to do is to write an alternative function to return the last element of a list by recursing through the list until the tail is the empty set and then returning the head. I have...

mylast [] = []
mylast (x:[]) = x
mylast (x:xs) = mylast xs

...but I get an error when I try any non-empty list :( Any suggestions as to what I have done wrong? TIA.

+7  A: 

Try mylast [] = error "Empty list!" instead. Otherwise Haskell cannot infer the type of your function.

EFraim
+5  A: 

EFraim's solution should work (up-voted). But I think this is a little more "Haskell-like":

mylast [] = Nothing
mylast (x:[]) = Just x
mylast (x:xs) = mylast xs

Disclaimer: I haven't actually tried this. I may have made syntax errors.

Craig Stuntz
The middle case is the same as [x], right? IOW, the one element list created by concatenating x with the empty list "(x:[])" is the same as the list containing only x "[x]". I don't know which one is more idiomatic, though.
Jörg W Mittag
No, Just x is not the same as [x].
Craig Stuntz
Oh, wait, I see what you mean. Yes, the middle case is the list containing only one x. But all lists can be expressed as a head and a tail, even if one or both are empty.
Craig Stuntz
@Jorg - To say [x] in the middle case would not limit it to one element, because that x might stand for a list on its own. So x:[] is the correct designation. It tells the compiler that you want to match against a single element concatenated onto an empty list -- which guarantees a one-element list.
rtperson
@rtperson - If the x stands for a list, then [x] would still be a one-element list, where that one element just so happens to be a list, wouldn't it? Damn, I really need to get my Haskell environment back up and running.
Jörg W Mittag
@Jorg - The type signature on ['a'] and [1] are ['a'] :: [Char] and [1] :: (Num t) => [t] respectively. These both say to me "list of whatever." But I just tried it your way in the function, and it works, so you're right. Personally, I'd still go with the concatenation, just to make the underlying intent clear.
rtperson
@Jorg - Yes, `[x]` is always an one-element list.
Alexey Romanov
+19  A: 

The problem -- like so many others when you're learning Haskell -- is one of typing. Type the following into GHCi

:t mylast

and you'll see that the type signature is

mylast :: [[a]] -> [a]

which expects a list of lists and will return a list. So if you put in a list of strings ["bob", "fence", "house"] the function will work as you've written it.

The problem is your base case: mylast [] = [], which tells the compiler that you want to return a list. You want to return an element, not a list. But there is no empty element in Haskell (very much by design), so you need to use the Maybe monad.

mylast :: [a] -> Maybe a
mylast [] = Nothing
mylast (x:[]) = Just x
mylast (x:xs) = mylast xs

Monads are a somewhat abstract topic, but you need the Maybe monad when you're starting out. All you need to know about it is it's a type declaration that tells the compiler to expect two possibilities: "Nothing," or "Just x". The returning code can then take x and run with it, but if you leave off the "Just," the compiler will complain.

The alternative is to throw an error when an empty list is encountered, like so:

mynextlast [] = error "no empty lists allowed"
mynextlast (x:[])  = x
mynextlast (x:xs) = mynextlast xs

But my suspicion is that Maybe is the way to go.

rtperson
a lesson from this is that it is useful to specify the types of stuff you define. it will make the compilation catch bugs such as this one..
yairchu
Thanks for the answers everyone. I have tried...mylast3 :: [a] -> Maybe amylast3 [] = Nothingmylast3 (x:[]) = Just xmylast3 (x:xs) = mylast3 xsand I get eg...Main> mylast3 [2,4,66,5,4,33]Just 33 :: Maybe IntegerIs there anyway of making it not print the 'just' ?
Thanks for the answers everyone. I have tried... mylast3 :: [a] -> Maybe a mylast3 [] = Nothing mylast3 (x:[]) = Just x mylast3 (x:xs) = mylast3 xs and I get eg...Main> mylast3 [2,4,66,5,4,33] Just 33 :: Maybe Integer Is there anyway of making it not print the 'just' ?
willingly950: one way would be to use Data.Maybe.fromJust. it will throw an exception when it is Nothing.
yairchu
A: 

Thanks for the answers everyone. I have tried...

mylast :: [a] -> Maybe a 
mylast [] = Nothing 
mylast (x:[]) = Just x 
mylast (x:xs) = mylast xs

and I get eg...

Main> mylast3 [2,4,66,5,4,33] 
Just 33 :: Maybe Integer

Is there anyway of making it not print the 'just' in the answer?

[EDIT: Jörg W Mittag] (Comments are awful for posting code ...)

Here's how the entire code looks in context:

mylast []     = Nothing
mylast [x]    = Just x
mylast (x:xs) = mylast xs

mylook (Just a) = do print a
mylook Nothing  = do error "Nothing to see here, move along!"

mylook $ mylast [2,4,66,5,4,33]
The point of the Maybe Monad (and what distinguishes it from, say, NULL pointers in C or null references in Java), is that the type signature explicitly forces you to deal with both cases: that something was returned and that nothing was returned. So, you will have to explicitly pattern match on both cases and deal with them seperately. E.g. if you want to print the result: mylook (Just a) = do print a !NEWLINE! mylook Nothing = do error "Nothing to see here, move along!" !NEWLINE! mylook $ mylast [2,4,66,5,4,33]
Jörg W Mittag
`x <- mylast3 [2, 4, 66, 5, 4, 33]`
rampion
how about "fromJust $ mylast [2,4,66,5,4,33]"? (fromJust is imported from Data.Maybe)
newacct