3
votes

I am starting to program with haskell. The program I am developing just sums the total of a list with two elementes, for example:

[("book",10),("cookies",2),("icecream",5)]

This should return "17". Here i my code:

total [] = []
total ([("c",e)]:y) = total y ++ [e]

But while running in GHCi it gives me this error:

<interactive>:80:8:
    Couldn't match expected type `[([Char], a0)]'
                with actual type `([Char], t0)'
    In the expression: ("livro", 10)
    In the first argument of `total', namely
      `[("livro", 10), ("bolachas", 2), ("gelado", 5)]'
    In the expression:
      total [("livro", 10), ("bolachas", 2), ("gelado", 5)]

<interactive>:80:21:
    Couldn't match expected type `[([Char], a0)]'
                with actual type `([Char], t1)'
    In the expression: ("bolachas", 2)
    In the first argument of `total', namely
      `[("livro", 10), ("bolachas", 2), ("gelado", 5)]'
    In the expression:
      total [("livro", 10), ("bolachas", 2), ("gelado", 5)]

<interactive>:80:36:
    Couldn't match expected type `[([Char], a0)]'
                with actual type `([Char], t2)'
    In the expression: ("gelado", 5)
    In the first argument of `total', namely
      `[("livro", 10), ("bolachas", 2), ("gelado", 5)]'
    In the expression:
      total [("livro", 10), ("bolachas", 2), ("gelado", 5)]

This is probably very simple but as a beginner I was not able to solve this.

2

2 Answers

6
votes

In the line:

total ([("c",e)]:y) = total y ++ [e]

the ([("c",e)]:y) does not do what you want. It matches a nonempty list in which the first element is also a list (because of the [...]) and in which that sublist has exactly one element, which is a pair whose first element equals "c". In order to match what you want, you need to write:

total ((c,e):y) = total y ++ [e]

However, this still won't do what you want, as it constructs a list of all of the e values in the input list. To sum them together, you need to do:

total [] = 0
total ((c,e):y) = total y + e
4
votes

In addition to what @jwodder said, note that there's also another way to approach the problem. Instead of thinking /how/ you would compute the desired value, think about /what/ you do: you take the second element of every list item and then sum those elements up.

So you could start by writing two functions, one which takes a list of tuples yielding a list of all the second elements, and another computing the sum of a given list of numbers. A good start is to come up with the type signatures but defining the functions to evaluate to undefined:

-- Takes a list of tuples and returns all the second elements
getSecondElements :: [(String, Int)] -> [Int]
getSecondElements someList = undefined

-- Computes the sum of a given list of integers
sumOfIntList :: [Int] -> Int
sumOfIntList someInts = undefined

Using these, defining you function is straightforward:

total myList = sumOfIntList (getSecondElements myList)

You can run this through ghci and it'll type-check, which is a good sign. When you try to actually use total, you get an error though because the other two functions are just undefined.

Before you go and think about how to implement them, it's a good idea to see whether they exist already. You can search Hoogle for type signatures and it'll dig up functions matching that signature. The signature of getSecondElements doesn't yield any hits but the signature for the second yields a lot of hits, and most of those hits don't even talk about Int at all: Hoogle is smart enough to understand that functions which deal on arbitrary types of lists (say: length or head) may also be applicable. If you scroll down the page you'll see that there's an existing sum function already!

For the first function, you can repeat the process (recursively, if you like): in order to get all second-tuple-elements in a list, you first need a function which gets the second element of a tuple, like

getSecondElement :: (String, Int) -> Int
getSecondElement = undefined

and another function which applies that to all elements of a list. I'll skip ahead a bit: the standard function for getting the second element of a 2-tuple is called snd, and the function for collecting the results of calling a function on all elements of a list is called map. Try running

:t map snd

in ghci to see the type of map snd:

map snd :: [(a, b)] -> [b]

...which is a generalized version of the type of our getSecondElements function! So the two missing pieces are map snd and sum, which gives:

-- Takes a list of tuples and returns all the second elements
getSecondElements :: [(String, Int)] -> [Int]
getSecondElements someList = map snd someList

 -- Computes the sum of a given list of integers
sumOfIntList :: [Int] -> Int
sumOfIntList someInts = sum

Instead of having two extra functions, you can also define total in terms of map snd and sum directly:

total someList = sum (map snd someList)

...which can be shortened to

total = sum . map snd

Not bad, is it?