0

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.

Jack Parkinson
  • 681
  • 11
  • 35
  • 1
    This is a great programming exercise. But [you might enjoy this cautionary tale about names](https://www.kalzumeus.com/2010/06/17/falsehoods-programmers-believe-about-names/). – Daniel Wagner Dec 06 '16 at 21:30
  • @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 Dec 06 '16 at 23:54

2 Answers2

5

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
chepner
  • 497,756
  • 71
  • 530
  • 681
  • I'm not sure I fully understand why return behaves like this, but I think it's because I'm not thinking about it in a functional context based on my coding background. I didn't realise that using `do ... return ...` would yield any different result from using a regular function call but I will look into the documentation more. Thanks for your response! – Jack Parkinson Dec 06 '16 at 17:24
  • 2
    That is the purpose of `return`; its general type is `return :: Monad m => a -> m a`, but `m` here is fixed to be `[]` because of the declared return value of `getname`. The `return` function is nothing like the statement of the same name in most programming languages – chepner Dec 06 '16 at 18:12
  • Yes, so it would seem, I think that's what threw me off. I expected it to behave the way I've always found it to behave. – Jack Parkinson Dec 06 '16 at 22:33
2

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
chi
  • 111,837
  • 3
  • 133
  • 218
  • Thanks for your response - in regard to safeLast, maybe you should actually suggest it to the Haskell community. Or failing that, I think they let just about everything on hackage! – Jack Parkinson Dec 07 '16 at 00:06