Hi folks. I decided to learn a functional language in order to increase my programming knowledge and was advised to learn Haskell. I didn't find any useful books, so one of my friends (former software engineering student) gave me his class notes, including tons of exercises.
However, he claimed he "lost" the solutions for the said exercises because they were stored in a pen, which he forgot where he placed it. :(

I'm having a hard time to get this function to work because the error message is confusing (which is my main Haskell pet peeve).

In this case, I just want a simple thing, I want to sum two lists.

And so I wrote down:

sum2lists :: [Int] -> [Int] -> [Int]
sum2lists list1 list2
	| null list2 = list2
	| list1 null = list1
	| otherwise = head list1 + head list2 : (sum2lists tail(list1) tail(list2))

However, everytime I try to run the program I get the following error message:

ERROR file:.\sum2lists.hs:4 - Type error in application
*** Expression : list1 null
*** Term : list1
*** Type : [Int]
*** Does not match : a -> b

I already Google'd but found nothing useful.

P.S: Do you know any good Haskell books, especially with solved exercises and such? For sites, I usually visit haskell.org and zvon.org.

Thanks for your time!

Member Avatar

Hiya. Someone mentioned your plight on #haskell, so I thought I'd register a throw-away account and help you out (to maintain the Haskell community's rep for being aggressively helpful...).

Before I start, a few prefatory points:

It looks to me like you are using the Hugs interpreter. This isn't a particularly good idea. Hugs used to be a good choice, but over the long years, it just hasn't really kept up with the state of the art. In this case, the error message isn't really helpful - what you want to know is that you mistakenly sent a bunch of Integers to do a function's job. That's what the three lines mean. This would be clearer if you were using something like the Helium teaching compiler (http://www.cs.uu.nl/helium/), or GHC which gives you the nice error message:

Couldn't match expected type `([a] -> Bool) -> Bool'
against inferred type `[Int]'

('Expected' = what the expression *must* have, and 'inferred' = what you actually gave it).

Secondly, I don't know much about Daniweb, but I think I can guarantee that the #haskell IRC channel on FreeNode and the haskell-cafe mailing list (http://www.haskell.org/mailman/listinfo/haskell-cafe) are the best places online to ask for haskell help, bar none. I found them very very helpful when I was starting out myself. Use them! Also, better resources for learning Haskell than your mentioned links are two books line: http://en.wikibooks.org/wiki/Haskell/YAHT and http://en.wikibooks.org/wiki/Haskell

On to your problem. The first error is this:

sum2lists list1 list2
| null list2 = list2
| list1 null = list1

OK, the name is right, the args are well-named, the guard is syntactically right... I personally would write 'list2 == []' myself, since I had to look up what null is, but null works. But, wait, what's this? 'list1 null'? Right where the error is identified... 'list1' is just some numbers, how can it 'do' anything with a function like null? You obviously meant 'null list1'. So:

sum2lists :: [Int] -> [Int] -> [Int]
sum2lists list1 list2
| null list2 = list2
| null list1 = list1

We could change this a little. I'm not sure this is correct behaivour. If one argument is empty, you want to return an empty argument?

So 'sum2lists [1..10] []' ~> []?

Well, alright. If a list is 'null', that means it is [], right? We can pattern-match on []. You might have a clearer definition of those two guards like this:

sum2lists [] _ = []
sum2lists _ [] = []

Isn't that nice?

But this still isn't right.

The last guard, the actual recursion, is a bit tricky. The right answer looks more like

| otherwise = ((head list1) + (head list2)) : (sum2lists (tail list1) (tail list2))

instead of

| otherwise = head list1 + head list2 : (sum2lists tail(list1) tail(list2))

What you have to remember is that when in doubt, parenthesize. In this case, the trouble is in '(sum2lists tail(list1) tail(list2))'. sum2lists is taking the 'tail's as arguments, and not the result of applying tails to the lists. (remember, functions are first-class objects, you can pass'em around and use them as you please.) Change the parentheization a little to make it very clear that tail should apply to the list before sum2lists ever sees them, and you're good.

But all this heads and tails stuff just obfuscates matters. It looked so much nicer when we switched to pattern-matching for empty lists, so let's do patter-match the rest:

sum2lists :: [Int] -> [Int] -> [Int]
sum2lists [] _ = [] -- as before
sum2lists _ [] = []
sum2lists (x:xs) (y:ys) = (x + y) : sum2lists xs ys


This wouldn't be complete if I didn't show you an even better way to write such a function.

Any Haskeller worth their salt, on seeing some function which is combining multiple lists into a single list, should immediately think to themself: 'How can I do this with a "zip" function?"

(Zips match elements of a list to respective entries in other lists, think like a zipper.)

Now, 'zip' just takes two lists and makes a single list of tuples, but you want to apply a function and have a result list of Ints. You *could* go 'zip list1 list2', and then 'map (uncurry (+))' over it, so that your final definition looked like 'map (uncurry (+)) $ zip list1 list2' - but I don't find that very clear, do you?

You want 'zipWith'! So, you 'zipWith list1 list2', but what's the With? An addition function of course!

sum2lists list1 list2 = zipWith (+) list1 list2

If we wanted to, we could go all algebraic on this expression and drop the parameters from both sides of the equations (x + 1 = x + 1 == x = x):

sum2lists = zipWith (+)

Bwa ha ha ha!

(I cheated a little. This has a more general type signature - it'll take two lists of any kind of number and sum them. But you could keep the old type signature if you really wanted to restrict it to Ints or Integers or whatever.)

Woah! Thanks for help gwern!

The program now runs fine, but I have yet to study that zip function, along that (+)...it seems to make the programs considerably shorter.

I used WinHugs because it was what he used. But I decided to give Helium a spin and it seems great. Thanks for telling me about it.:)
WinHugs sometimes made me insane with his cryptic error messages...

P.S: It seems Helium likes to create an additional file (.vlm extension) when I save my programs. Can I switch this off?

Once again, thanks for the help. :)

Now let´s see how long I can program until I hit another wall. :D

Hi, I'd like to second the point out that you should come and visit us on IRC, specifically on irc.freenode.net, #haskell. (I'd put a smiley here, but this board would mangle it into some horrifying icon, so I'll avoid that.)

Helium has nice error messages, so it's good for beginners, but it's not actually a full Haskell implementation (so it lacks even things like typeclasses), so eventually, you'll certainly want to move to GHC.

Another subtlety (which I've pointed out to gwern) is that null really is better than ([] ==) for testing if you have an empty list. The latter requires that there be an instance of the class Eq for the list type, which means that there must be an instance of Eq for the elements of the list as well. The null function just does a pattern match, so it doesn't need that instance.

([] ==) :: (Eq a) => [a] -> Bool

null :: [a] -> Bool

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, learning, and sharing knowledge.