Generating output in a running haskell program
Coming from (SWI) Prolog I find it very difficult to have Haskell give output on the fly.
The simplest example, I'd like Haskell to print something on every iteration:
fac 0 = 1
fac n = fac ( n-1 ) * n
Or I would like to get output from a program that never halts...
-- A possible halt statement...
-- find_primes l 100 = l
find_primes l n = if ( is_prime l n ) then find_primes nn s else find_primes l s
where s = n + 1
nn = n:l
is_prime :: Integral a => [a] -> a -> Bool
is_prime [] n = True --print the prime number on the fly
is_pr开发者_如何学编程ime (h:t) n = if ( r /= 0 ) then is_prime t n else False
where r = n mod h
Prelude> find_primes [ ] 2
You have three options:
First: spread IO
everywhere and write in Haskell as in fancy imperative language.
fac 0 = putStrLn "0! = 1" >> return 1
fac n = do
x <- fac (n - 1)
let r = x * n
putStrLn $ show n ++ "! = " ++ show r
return r
Second: use unsafePerformIO
and its derivatives (e.g. Debug.Trace
). Useful for shooting yourself in the foot and debugging purposes.
Third: instead of mixing I/O and computation in the code, lazily generate a [potentially infinite] data structure containing intermediate results in a pure function and consume it separately. For example, the infinite list of factorials can be written as:
facs = scanl (*) 1 [1..]
And consumed as follows to yield the same result as in calling fac 10
in the example above:
forM_ (take 11 $ zip [0..] facs) $ \(i, x) ->
putStrLn $ show i ++ "! = " ++ show x
There's a couple more options that rkhayrov didn't cover in their answer
If you just want debug information, consider using Debug.Trace
's trace
function.
import Debug.Trace
fact :: (Ord a, Num a) => a -> a
fact n | n > 1 = traceShow n $ n * fact (n-1)
| otherwise = traceShow n $ 1
I can't recommend using that for more than bug-hunting, though. It definitely shouldn't go into production code.
If you want to log information while you work, consider using the Writer Monad.
import Control.Monad.Writer.Lazy
import Control.Applicative ((<$>))
fact :: (Ord a, Num a) => a -> Writer [String] a
fact n = do
tell $ [show n]
if n > 1
then
(n*) <$> fact (n-1)
else
return 1
You could also go more general and use the MonadWriter
typeclass.
精彩评论