You have three options:
First: distribute IO everywhere and write in Haskell, as in a bizarre 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 legs and debugging purposes.
Thirdly: instead of mixing I / O and computing in the code, it is lazy to generate a [potentially infinite] data structure containing intermediate results in a pure function and consume it separately. For example, an endless list of factorials can be written as:
facs = scanl (*) 1 [1..]
And it is consumed as follows to get the same result as when calling fac 10 in the above example:
forM_ (take 11 $ zip [0..] facs) $ \(i, x) -> putStrLn $ show i ++ "! = " ++ show x
source share