Can someone explain to me what is wrong with the logic

I need a function that sum all the digits as follow: sumDigits [16,7,12,5] = 1+6+7+1+2+5 = 22

I have defined toDigits function as follow:

    toDigits :: Integer -> [Integer]
    toDigits n
        | n <= 0 = []
        | otherwise = toDigits(n `div` 10) ++ [n `mod` 10]

The sumDigits function is defined as follow:

    sumDigits :: [Integer] -> Integer
    sumDigits [] = 0
    sumDigits (x:[]) = x
    sumDigits (x:zs) = (sumDigits . toDigits x) + sumDigits zs

The compiler shows the following errors:

Couldn't match expected type ‘a0 -> [Integer]’
              with actual type ‘[Integer]’
Possible cause: ‘toDigits’ is applied to too many arguments
  In the second argument of ‘(.)’, namely ‘toDigits x’
  In the first argument of ‘(+)’, namely ‘(sumDigits . toDigits x)’
  In the expression: (sumDigits . toDigits x) + sumDigitsX zs

Couldn't match expected type ‘a0 -> Integer’
              with actual type ‘Integer’
Possible cause: ‘sumDigitsX’ is applied to too many arguments
  In the second argument of ‘(+)’, namely ‘sumDigits zs’
  In the expression: (sumDigits . toDigits x) + sumDigits zs
  In an equation for ‘sumDigits’:
      sumDigitsX (x : zs) = (sumDigits . toDigits x) + sumDigits zs

I expect that toDigits will split each number to a singleton array and sumDigits will convert it to Integer. Can you explain what is wrong with this one?

Upvotes: 1

Views: 90

Answers (2)

Chris Smith
Chris Smith

Reputation: 2645

The . operator is function composition, and expects the expressions on each side to be functions. When you write (sumDigits . toDigits x), the left side is sumDigits, which is a function. But the right side is toDigits x, which is not a function. (toDigits is a function, but toDigits x is the result after that function has been applied.)

It seems that you meant (sumDigits . toDigits) x. That composes the functions first, and then applies the resulting function to x, computing the sum of its digits. In Haskell, function application has a higher precedence than anything else, so you need the parentheses to group the function composition first, and then apply it.

Upvotes: 4

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477616

The expression sumDigits . toDigits x is interpreted as sumDigits . (toDigits x). Since the signature of (.) is (.) :: (b -> c) -> (a -> b) -> a -> c, the result thus should be a function, not a value.

That being said, it does not look a very good approach here:

  1. you implement a lot of logic yourself, for example summing up items; and
  2. the toDigits function runs in O(n2) with n the number of digits, since you each time append, instead of prepending.

Since the order of the digits does not matter, we can implement toDigits as:

toDigits :: Integral i => i -> [i]
toDigits n | n <= 0 = []
           | otherwise = r : toDigits q
           where (q,r) = quotRem n 10

and we can then calculate the sum of the digits as:

sumDigits :: Integral i => [i] -> i
sumDigits = sum . concatMap toDigits

we here thus concatenate a mapping with toDigits, and then calculate the sum over that result.

This gives us the expected:

Prelude> sumDigits [16,7,12,5]
22

Upvotes: 4

Related Questions