5

I am trying to get a Haskell function to show whenever it is applied by adding a call to "putStrLn":

isPrime2 1 = False

isPrime2 n = do
    putStrLn n
    null (filter (==0) (map (mod n) (filter isPrime2 [2..(floor (sqrt(fromIntegral (n-1))))])))

(The ultimate goal is to demonstrate why one version of isPrime is more efficient than another.)

When I load the above code into GHCi, I get the error:

Couldn't match expected type Bool with actual type m0 b0

I'm sure this is a n00b mistake. Could someone tell me the right way to accomplish what I'm trying to do?

fuz
  • 88,405
  • 25
  • 200
  • 352
Ellen Spertus
  • 6,576
  • 9
  • 50
  • 101

2 Answers2

18

The problem is, that Haskell has a strict distinction between pure functions such as (+) and map and impure actions such as putStrLn and main. A pure function is supposed to always yield the same result when given the same input and not modifying anything. This obviously prohibits uses of PutStr and friends. The type system actually enforces this separation. Each function that does IO or is impure in any way has an IO sticking in front of it's type.


tl;dr; use trace from the module Debug.Trace:

import Debug.Trace

isPrime2 1 = False
isPrime2 n = show n `trace` null (filter (==0) (map (mod n) (filter isPrime2 [2..(floor (sqrt(fromIntegral (n-1))))])))

But beware that the results may be rather surprising as there is no guarantee that your code will actually run; the argument of trace may run once or twice or any other number of times.

fuz
  • 88,405
  • 25
  • 200
  • 352
4

Whenever you have these kinds of type errors like Couldn't match expected type X with actual type Y you should use the haskell type system to guide you.
So let's see what is the problem:

You have a pure function with the type Int -> Bool. And you want to print some debug output which is clearly not pure (i.e. which lives in the IO Monad).
But anyway what you want to write is s.th. along those lines:

foo x 
  | x > 0 = debug "ok" True
  | otherwise = debug "ohhh...no" False

Still, the type of your function should be foo :: Int -> Bool

So let's define a debug function that will satisfy the type-checker. It would have to take a String (your debug message) and a Bool (your result) and only evaluate to the Bool.

debug :: String -> Bool -> Bool
debug = undefined

But if we try to implement it, it kind of does not work since we can't escape the IO Monad since the type of putStrLn is putStrLn :: String -> IO (). In order to combine it with evaluating to a Bool we will have to put the Bool in the context of the IO too:

debugIO msg result = putStrLn msg >> return result

Ok, let's ask ghci for the type of this function:

Main> :t debugIO
debugIO :: String -> b -> IO b

So we get an IO Bool but would need just a Bool.
Is there a function with the type IO b -> b? A quick lookup on hoogle gives us a hint:

The infamous unsafePerformIO :: IO a -> a has the type we need here.
So now we could implement our debug function in terms of debugIO:

debug :: String -> Bool -> Bool
debug s r = unsafePerformIO $ debugIO s r

which actually is pretty much what you get with the trace function in the Debug.Trace package as already pointed out by FUZxxl.
And since we agree that one should never use unsafePerformIO the usage of the trace function is preferred. Just keep in mind that despite it's pure type signature it actually is also not referential transparent and uses unsafePerformIO underneath.

oliver
  • 9,235
  • 4
  • 34
  • 39
  • Please note, that you should **never** use the function `unsafePerformIO`. There is nearly *always* a better solution. This function is evil; it allows you to do things that are otherwise impossible and to destroy some safety assumptions the compiler could make otherwise. If you use `unsafePerformIO`, think twice and if you are not 100% sure, *don't use it*! There are some legitimate use cases, like `trace`, but they are rare. – fuz Oct 04 '11 at 17:35
  • @FUZxxl: I'm well aware of the merits and dangers of `unsafePerformIO`. But in order to implement what espertus was asking for I don't see any other option. As you pointed out, `trace` _is_ in fact implemented in terms of `unsafePerformIO`. I'd say it falls into the same category of code that should usually be treated with a long stick. – oliver Oct 04 '11 at 17:42
  • And would you also say that you should never use `trace`, except for debugging? For example, you should never use it to print information that users are supposed to read when the code is in production? – Tyler Oct 04 '11 at 17:43
  • @MatrixFrog: exactly. `trace` should only be used for quick and dirty debugging. (with an emphasis on _dirty_). In situations where you are dealing with user visible output you most probably already have to run some code in the IO Monad. The `trace` function is, like `unsafePerformIO`, not referentially transparent. You might not always see your user messages as you would expect them. – oliver Oct 04 '11 at 18:05
  • If you just enter the type of your `debug` function into Hoogle, you get `trace` as the first hit, so you could have stopped there instead of introducing `unsafePerformIO` (the function whose name must never be spoken!) – pat Oct 04 '11 at 18:35
  • 1
    @oliver Can I add a small remark that one should never ever use The Function That Must Not Be Named? – fuz Oct 04 '11 at 18:56
  • @FUZxxl sure. Programming haskell you should never use it. In the case of library writers I'm actually not quite sure...afaict there are usecases where it makes sense. But I'll add your remark to make that more clear. – oliver Oct 05 '11 at 05:43