Lazy IO is usually implemented using unsafeInterleaveIO :: IO a -> IO a , which delays the side effects of an I / O action until its result is achieved, so we will probably have to use this, but try some minor problems first .
First of all, lift putStr will not enter a check because putStr is of type String -> IO () , and lift is of type IO a -> M a . Instead, we need to use something like lift . putStr lift . putStr .
Secondly, we will need to distinguish between IO actions that should be lazy and those that should not. Otherwise, putStr will never be executed, since we are not using its return value () anywhere.
Given this, this seems to work for your simple example, at least.
{-
However, as C. A. McCann points out , you probably shouldn't use this for anything serious. Lazy IO frowned already, as this makes it difficult to reason about the actual order of side effects. That would make it even harder.
Consider this example
main = runM $ do foo <- lazy readLn bar <- lazy readLn return $ foo / bar
The reading order of the two numbers will be completely undefined and may vary depending on the compiler version, optimization or alignment of stars. The name unsafeInterleaveIO long and ugly for a good reason: to remind you of the dangers of its use. It is a good idea to let people know when they are being used, rather than hiding them in the monad.
source share