Raul Paredes
Raul Paredes

Reputation: 19

getting the last element in list without using pattern matching

I keep getting "error: parse error on input ‘|’ " can someone tell me why? I already rewrote it to make sure all spaces where correct here is my code:

 mylast :: (Eq a) => [a] -> [a]  
 mylast [] = []  
 mylast (x:xs)  
   | xs == [] : x  
   | otherwise = mylast xs   

Upvotes: 1

Views: 114

Answers (2)

dfeuer
dfeuer

Reputation: 48580

If you really want to avoid writing any patterns, the simplest way is probably foldl':

mylast :: [a] -> Maybe a
mylast = foldl' (\_ x -> Just x) Nothing

If you prefer, you can reach down to the primitive catamorphism on lists, foldr:

mylast :: [a] -> Maybe a
mylast xs = foldr (\x r _ -> r (Just x)) id xs Nothing

but that's a bit trickier to understand.

foldl' and foldr are ultimately implemented using pattern matching, because that is the fundamental low-level way to inspect a list in Haskell. So there isn't any way to really avoid it altogether.

Upvotes: 1

Daniel Wagner
Daniel Wagner

Reputation: 152682

The minimal change needed to fix that exact error is just to fix what is probably a typo: you wrote xs == [] : x, and probably meant xs == [] = x instead. So:

mylast :: (Eq a) => [a] -> [a]
mylast [] = []
mylast (x:xs)
  | xs == [] = x
  | otherwise = mylast xs

This will give you a type error, because x is a list element, and you say mylast returns a list. You can fix this by making it a singleton list [x], as in:

mylast :: (Eq a) => [a] -> [a]
mylast [] = []
mylast (x:xs)
  | xs == [] = [x]
  | otherwise = mylast xs

From there, I have some stylistic comments.

  1. I like your idea to return a list so that you don't need to throw an error if the input is empty. But since we're definitely returning at most one element, you might consider using Maybe for this role instead of []; it gives a type-level guarantee of at most one element.
  2. The Eq a constraint is unfortunate: it arises because you write xs == [], which doesn't actually need to call (==) on any elements, but nevertheless requires the user to provide an implementation of (==) on elements. The standard way to check if a list is empty is either to call null or to directly use pattern matching, neither of which require Eq.

Combining these two ideas, we get:

mylast :: [a] -> Maybe a
mylast [] = Nothing
mylast [x] = Just x -- convenient syntax sugar for mylast (x:[]) = Just x
mylast (x:xs) = mylast xs

This looks very idiomatic to me.

Upvotes: 7

Related Questions