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.
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).
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:
| 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:
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.)
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.