haskell: error trying to call putStrLn in function

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

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'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.


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]" (ie 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.

链接地址: http://www.djcxy.com/p/5668.html

上一篇: Haskell / GHC中的`forall`关键字是做什么的?

下一篇: haskell:尝试在函数中调用putStrLn时出错