Search code examples
haskelloption-type

Haskell Maybe list element operations


I'm learning list operations in Haskell and now I'm trying out various list operations on Maybe list type. Currently, I have this implementation of sum of elements in list in Haskell

sum :: Num a => [a] -> a
sum [] = 0
sum (a:t) = a + sum t

Now I want to do the same thing but instead of returning the value, I want to return a Maybe type instead. And when the list given is empty it should return Nothing.

I've come up with this

sum :: Num a => [a] -> Maybe a
sum [] = Nothing
sum (a:t) = fmap (a+) (sum t)

But the result of all non empty list given results in Nothing.

From what I understand, the list given will eventually pattern match with the empty list therefore returning Nothing.

How do I fix this so that it returns the expected value and of Maybe type. I can't figure out how to make it work recursively as the normal sum implementation above, so I guess there should be another way. Would prefer if only Prelude module is imported as I'm still trying to absorb stuff inside the Prelude module.


Solution

  • The problem is that your recursive function always uses the empty list as a base case, so your base case value is always Nothing. You only want to return Nothing if the "root" call takes an empty list. Otherwise, you want to use the singleton list as your base case.

    sum :: Num a => [a] -> Maybe a
    sum [] = Nothing
    sum [x] = Just x
    sum (a:t) = fmap (a+) (sum t)

    This way, sum [] will never be reached by a recursive call; any non-empty list will hit the base case sum [x] first.

    Another way to organize this is to use your original total function as a helper that sums non-empty lists only, and handle the empty list separately.

    sum :: Num a => [a] -> Maybe a
    sum [] = Nothing
    sum xs = sum' xs
      where sum' [] = Just 0
            sum' (a:t) = fmap (a+) (sum' t)
    

    Note that sum' can be called on an empty list, but only if it is originally called on a non-empty list.

    As @chi points out, the helper function doesn't need to use Maybe at all; since it only sums non-empty lists, you can skip using fmap and sum the list normally; only the final result needs to be wrapped in Just:

    sum :: Num a => [a] -> Maybe a
    sum [] = Nothing
    sum xs = Just (sum' xs)
             where sum' [] = 0
                   sum' (a:t) = a + sum' t