0
votes

I am trying to write a Haskell function which would take a person's full name and return only the last name; for example, getName "Hans Christian Anderson" should return "Anderson". The function is below:

getname :: String -> String
getname fullName = do
  let nameList = splitOn " " fullName    -- split the full name into individual names
  let reversedName = reverse nameList    -- reverse the full name
  let lastName = reversedName !! 0       -- get the first name from the reversed list
  return lastName

But whenever I try to compile this, I get the following error:

Couldn't match expected type ‘Char’ with actual type ‘[Char]’
In the first argument of ‘return’, namely ‘lastName’
In a stmt of a 'do' block: return lastName

I'm not sure I fully understand this error. As I understand Haskell (I am quite new to it) the structure [Char] would be the same as String, which is what I'm looking for. I just don't understand why it's expecting this Char type that it seems is appearing in the return statement as a String. I have gone over each line and they seem to be correct to me.

Any advice on why this behavior is occurring and how to fix it is much appreciated.

2
This is a great programming exercise. But you might enjoy this cautionary tale about names.Daniel Wagner
@DanielWagner I have seen something like that before, and in fact I did consider how cautious I needed to be about the names I was dealing with so as not to get erroneous results. However, for the purposes of this exercise, it was acceptable for me to be a little heavy handed and go with a lot of those assumptions.Jack Parkinson

2 Answers

5
votes

Don't use do notation here. Because the return value of your function is [Char], the argument to return is expected to be a Char value (because return :: a -> [a]).

You aren't doing anything here that requires do notation; just use regular function calls.

getname fullName = (reverse (splitOn " " fullName)) !! 0
2
votes

Your code is accidentally working inside the list monad, as if it was meant to describe a nondeterministic computation. You do not need that monad, nor any other monad. So, avoid using do for this. You can still use let .. in .. if you want:

getname :: String -> String
getname fullName =
  let nameList = splitOn " " fullName    -- split the full name into individual names
      reversedName = reverse nameList    -- reverse the full name
      lastName = reversedName !! 0       -- get the first name from the reversed list
  in lastName

Alternatively, use last:

getname :: String -> String
getname fullName = last (splitOn " " fullName)

Beware that last is partial: it will crash your program as soon as there is not name in the input string. A safer approach could be:

getname :: String -> Maybe String
getname fullName = case splitOn " " fullName of
   [] -> Nothing
   xs -> Just (last xs)

Or even:

getname :: String -> Maybe String
getname fullName = safeLast (splitOn " " fullName)

-- This should be already there in the library, IMO
safeLast :: [a] -> Maybe a
safeLast []     = Nothing
safeLast [x]    = Just x
safeLast (_:xs) = safeLast xs