3
votes

I am writing a program that sums the numbers of a list up:

[2,3,4,5] = 14

I get an error when I compile it and I have no clue what the program wants:

getLimit :: [Int] -> Int
getLimit [] = 0
getLimit [x] = sum [x]
getLimit [y:yz] = y + getLimit yz

The error is:

http://i.imgur.com/G06noq4.jpg

Thanks for any help in advance.

2
y:yz is a list. So [y:yz] is a list of lists. getLimit takes a list of Ints, not a list of lists of Ints ([[Int]]). Also, I think the [x] case should be eliminated.rici
If you are allowed to use sum then why not just produce the simple answer of getLimit = sum?Thomas M. DuBuisson
A lot of the errors that you get from the GHC compiler, you'll also get from the JVM, just later, once you run the program and get to that line of code. The errors are your friends.Boyd Stephen Smith Jr.
The Haskell compilers are indeed quite picky, and tend to complain a lot. While having more compile-time errors may be annoying, in return you get fewer run-time errors. Haskell programmers actually enjoy being forced to essentially prove their programs (partially) correct, since they know this effort will pay off in the long run, especially when building large programs.chi

2 Answers

5
votes

As mentioned in the comments, there's a couple of things you need to change in your code:

  1. [y:yz] represents a list of lists due to the square brackets. You need to replace it with parentheses to make it look like the head:tail pattern like so - (y:yz).
  2. Since you have already defined the base case for recursion as getLimit[] = 0, you can eliminate the test for single element lists, and directly go to the one for all non-empty lists, with the same expression.

To add to this, it would probably be better to use foldr, as this would make your code shorter, and with the same functionality. You can rewrite it like so:

getLimit :: [Int] -> Int
getLimit = foldr (\x acc -> x + acc) 0 

As to why I recommend foldr instead of say foldl', there's good explanation here.

1
votes

No, Haskell is wayyy easier to learn than Java. It has no NullPointerExceptions.

So let's compare the getLimit function in Haskell and in Java.

Haskell

getLimit :: [Int] -> Int
getLimit []     = 0
getLimit (x:xs) = x + getLimit xs

The problem with your implementation of getLimit was with the line getLimit [y:yz] = y + getLimit yz:

  1. The value y:yz is of type [Int]. Hence [y:yz] is of type [[Int]] which is wrong.
  2. The compiler told you that. Learn to read error messages. What you want is just (y:yz).
  3. The line getLimit [x] = sum [x] is unnecessary.

Java

public int getLimit(int xs[]) {
    int length = xs.length;
    if (length == 0) return 0;
    else return xs[0] + getLimit(Arrays.copyOfRange(xs, 1, length));
}

Of course in Java you would rather use for loops instead of recursion (which in my opinion is even worse because loops have no type and hence cannot be type checked by the compiler).

In contrast you can create looping constructs in Haskell which type check. For example the foldl function is defined as:

foldl :: (b -> a -> b) -> b -> [a] -> b
foldl _ a []     = a
foldl f a (x:xs) = foldl f (f a x) xs

This is equivalent to the following JavaScript code (sorry, I don't know how to use generics in Java):

function foldl(f, a, xs) {
    for (var i = 0, l = xs.length; i < xs; i++)
        a = f(a, xs[i]);
    return a;
}

In short we have given a particular use case of a for loop a type.

Conclusion

Haskell is much better than Java. One of the best features of Haskell is generics. For example we can make getLimit a generic function which works on all types of numbers (not just Ints):

getLimit :: Num a => [a] -> a
getLimit []     = 0
getLimit (x:xs) = x + getLimit xs

The only thing we changed was the type signature of the function. Everything else remained the same. Try doing the same thing in Java.