views:

785

answers:

4

I'm trying to put a 'print out' function call in a haskell function.

(a simple debug message).

Below is my code and error message from the compiler (ghc 6.10).

I don't quite understand why it is lumping the putstr call and the empty array.

The empty array is the return value for that particular case (the print out message is actually just a stub for now).

Any idea why this is isn't working?

Thanks

My Code:

isAFactor :: Integer -> Integer -> Bool 
isAFactor x y = x `mod` y == 0

findFactors :: Integer -> Integer -> [Integer]
findFactors counter num = 
    let quotient = div num 2
    in
     if(counter >  quotient)
      then do
       putStrLn ("factorList is  : " ++ show quotient)  (*** Line 10***)
       []
     else if(isAFactor num counter)
      then [counter] ++ [quotient] ++ findFactors (counter + 1) num
     else
      findFactors (counter + 1) num

Error from ghc

    test.hs:10:4:
    Couldn't match expected type `[a] -> [Integer]'
           against inferred type `IO ()'
    In the expression:
        putStrLn ("factorList is  : " ++ show quotient) []
    In the expression:
        do putStrLn ("factorList is  : " ++ show quotient) []
    In the expression:
        if (counter > quotient) then
            do putStrLn ("factorList is  : " ++ show quotient) []
        else
            if (isAFactor num counter) then
                  [counter] ++ [quotient] ++ findFactors (counter + 1) num
            else
                findFactors (counter + 1) num
+13  A: 

It is important to remember that Haskell is a pure functional language. This means that functions are not allowed to have any side effects at all, including printing debug messages to your screen.

It is however possible to break this purity and it can be useful in debugging. Take a look at the module Debug.Trace. There you will find a function trace :: String -> a -> a. You can use it in your code like this:

import Debug.Trace

isAFactor :: Integer -> Integer -> Bool 
isAFactor x y = x `mod` y == 0

findFactors :: Integer -> Integer -> [Integer]
findFactors counter num = 
    let quotient = div num 2
    in
        if(counter >  quotient)
             then trace ("factorList is: " ++ show quotient) [] 
        else if(isAFactor num counter)
         then [counter] ++ [quotient] ++ findFactors (counter + 1) num
        else
         findFactors (counter + 1) num

As the comments suggested:

Haskell is also a lazy language. An Expression is not evaluated before the result is actually needed. Using the trace function can be a bit confusing in a lazy setting because it is not always easy to understand when the trace message is printed to screen (if it is printed at all).

As haskell is a very different kind of language it is perhaps best to try and develop programs in a equally different way. Try to reason about your functions instead of using trace and similar "unpure" constructs. Learn to take advantage of haskells powerful type system and use (for example) QuickCheck to test your function once it passed the type checker.

Jonas
It's worth noting that because Haskell is a lazy language, these `trace` calls also occur lazily. Because there is no explicit sequencing of operations, the calls to `trace` might appear to be out of order or not even happen at all. Remember you're dealing with a lazy language, also when debugging.
Tom Lokhorst
+2  A: 

Updated with clarifications

The problem is that IO in Haskell is monadic, the block beginning with do is a syntactic sugar for combining monadic expressions (sometimes called statements) with monadic operators. In this case, the monad in question is the IO monad, as can be inferred from the call to putStrLn. The [] in the second line of the do block is actually not the value of the whole do block, rather, it is interpreted as the last argument of putStrLn; not that it accepts a second argument, but the compiler doesn't even get to the point of figuring this out, because it terminates earlier with the type error you quoted. To make that line a command, you'd have to put, e.g., return, another monadic function, in front of it (i.e., return []). I'm not saying, though, that this would help you to solve your problem.

The type error stems from the fact that IO monadic expressions always have the type IO _; in your case, the do block has this type as well, which is obviously incompatible with [Integer], the type you specified in the signature.

In general, because Haskell is a pure functional language with monadic IO, once you're inside the IO monad, there is no way out of it, it is contageous. i.e., if a function has a do block with IO operations in it, its signature will necessarily contain the IO _ type, and so will the signature of all other functions invoking this function, etc. (Other monads do provide "exit" functions, but the IO monad does not.)

David Hanak
It's not really interpreted as the last argument of putStrLn is it? It's the second argument of (>>=). do blocks aren't necessarily IO (), there are many different types that do blocks can have, including [a]!
cthulahoops
You've got a number of misconceptions here. I hate to downvote an earnest post, but this obscures more than it reveals.
Peter Burns
@cthulahoops: The error message 'In the expression "putStrLn ("factorList is : " ++ show quotient) []"' seems to indicate that my interpretation is correct.
David Hanak
@Peter: criticism accepted, rephrased whole answer to make myself clearer. Please reconsider your vote.
David Hanak
+6  A: 

Jonas's post covers your question pretty well, so I'll give you an idiomatic rewrite of your findFactors function. I found it helpful for me when I was first learning.

So you want to find all factors of a given number n by looking at each number from 1 up to n/2, checking to see if it's a factor of n and building a list of those that are.

Your version (with minimal modifications to get it to work):

findFactors :: Integer -> Integer -> [Integer]
findFactors counter num = 
    let quotient = div num 2
    in
        if(counter >  quotient)
         then []
        else if(isAFactor num counter)
         then [counter] ++ findFactors (counter + 1) num
        else
         findFactors (counter + 1) num

A couple of formatting changes to make it a bit more readable:

findFactors :: Integer -> Integer -> [Integer]
findFactors counter num
  | counter > div num 2 = []
  | otherwise = if num `isAFactor` counter 
                then counter:findFactors (counter+1) num
                else findFactors (counter + 1) num

This is fine, but it's less than ideal in a couple ways. First, it recalculates quotient each time findFactors is called, which is n/2 divisions (though ghc -O2 seems to realize this and calculate it only once). Second, it's kinda annoying to have to deal with that counter variable everywhere. Third, it's still pretty imperative.

Another way of looking at the problem would be to take the list of integers from 1 up to n/2 and filter for just those that are factors of n. This translates pretty directly into Haskell:

findFactors :: Integer -> [Integer]
findFactors num = filter (isAFactor num) [1..(num `div` 2)]

It might come as a surprise to find that this has the same performance characteristics as the above version. Haskell doesn't need to allocate memory for the entire list up to n/2 at once, it can just generate each value as it's needed.

Peter Burns
+5  A: 

Others have done a good job of explaining your code, so let me help you decode the error message.

Couldn't match expected type `[a] -> [Integer]'
       against inferred type `IO ()'
In the expression:
    putStrLn ("factorList is  : " ++ show quotient) []

I've missed off all the other "In the expression" parts; they just show more and more enclosing context.

Everything in Haskell is an expression, so therefore everything has a type. That includes something like "putStrLn". If you type ":t putStrLn" in GHCi you will see it reply:

putStrLn :: String -> IO ()

Which means that putStrLn is a function that takes a string and returns an "IO action", which in this case is the action of putting the message on the screen. In your code you gave "putStrLn" a string, so the compiler inferred that the expression "putStrLn (stuff)" had the type "IO ()". That is the "inferred type" part of the compiler error message.

Meanwhile the compiler was also doing type inference in the other direction, from the outside in. Amongst other things it noticed that the this "putStrLn (stuff)" expression seemed to be applied to an empty list, which has type "[a]" (i.e. a list of something, we don't know what). Furthermore the result of the whole expression should be of type "[Integer]". Therefore the expression "putStrLn (stuff)" should be a function to turn "[]" into a list of integers, the type of which is written "[a] -> [Integer]". That is the "expected type" part of the error message.

At this point the compiler concluded that it could not match up these two types, so it reported the error.

"Couldn't match expected type 'Foo' against inferred type 'Bar'" is probably the commonest error message you get when trying to compile Haskell, so its worth trying to read it and understand it. Look at the inferred type and try to figure out what part of the quoted expression has that type. Then try to figure out why the compiler expected something else by looking at the surrounding code.

Paul Johnson
+1, good explanation of the error message. I couldn't figure out how the code reported the error it did. Until I just realized that the questioner inserted a line break to insert the "line 10" comment.
Tom Lokhorst