Reputation: 427
I wrote the following code to find the last element of a list in haskell:
myLast (x:xs) = do
ret <- if xs == [] then x else (myLast xs)
return ret
The idea is to traverse the list until we are at an element which has the empty list as its next element. When we find it we set ret
to that element.
It makes sense for me but when I run the code inside the interactive shell I get the following error:
<interactive>:1:1: error:
• No instance for (Num (m0 b0)) arising from a use of ‘it’
• In a stmt of an interactive GHCi command: print it
edit 1
The reason I used do
was because I saw that pattern being used somewhere to also traverse a list, so I thought I could do the same here. I'am avoiding libraries for now to get comfortable with the language.
I wrote the function avoiding the do keyword and now it works:
myLast(x:xs) = if xs == [] then x else (myLast xs)
There's now just an issue with the empty list case. How to approach this in haskell?
Upvotes: 2
Views: 3090
Reputation: 71070
You want
myLast (x:xs) =
to be equal to
if xs == [] then x else (myLast xs)
Great, xs == []
, so let's just put it back in:
myLast (x:[]) = x
but what about the else
part? Well, let's add another equation for that,
myLast (_:xs) = myLast xs
and we're golden.
What if we call it with an empty list []
though? No definition case will match, and we will get some kind of a run-time error. Well, same thing happens with the built-in function last
too, so we're no better and no worse than Haskell itself here.
What is that match that I mentioned, you ask? That's how Haskell functions get invoked. Each function definition can have several clauses, starting with the function's name, and containing a pattern for each expected argument.
In a left hand side of an equation,
(x:[])
is a pattern, matching any singleton list. It can also be written [x]
. x
will refer to the list's only element, if used in the right-hand side of the equation.
[]
is a pattern, matching any empty list.
(x:xs)
is a pattern, matching any non-empty list. x
will refer to the list's head (i.e. first) element, if used in the right-hand side of the equation; and xs
will refer to the rest of the elements in a list (which are also, a list -- also known as its tail).
But wait, you ask. Wouldn't both clauses match for a singleton list, the first for the pattern [x]
and the second for (_:xs)
with xs
matched up with an empty list, []
?
Why yes, they both would match indeed; (x:[])
and (_:xs)
are not mutually exclusive.
But that's OK, because in Haskell, if the first clause has matched, that's it -- that is the clause that gets executed, and no other attempts at any additional pattern matching and clause selection are made.
That would be Prolog, and that's quite another language.
Upvotes: 1
Reputation: 67467
let's start with the signature of your function
myLast :: [a] -> a
now, for an empty list input, what can be expected as the output? How you can make up an instance of an arbitrary type a
?
Alternatively, you can defer the handling of missing last element to the callers of your function.
myLast :: [a] -> Maybe a
Upvotes: 2