Это невозможно для обычных монад, но для конкретного случая IO
это может быть достигнуто довольно легко (и сравнительно безопасно) с помощью unsafeInterleaveIO
, что делает IO-действие ленивым:
import System.IO.Unsafe
liftIO2Lazy :: (a -> b -> c) -> IO a -> IO b -> IO c
liftIO2Lazy f io1 io2 = do
x <- unsafeInterleaveIO io1
y <- unsafeInterleaveIO io2
return $ f x y
Результат будет ленивым в тех же аргументах, в которых f
ленив, поэтому он работает даже для функций, которые не следуют той же логике короткого замыкания слева направо, что и &&
и ||
:
ioTrue = putStrLn "TRUE" >> return True
ioFalse = putStrLn "FALSE" >> return False
liftIO2Lazy (&&) ioTrue ioFalse
-- Prints both messages
liftIO2Lazy (||) ioTrue ioFalse
-- Only prints TRUE
liftIO2Lazy (flip (||)) ioTrue ioFalse
-- Only prints FALSE
liftIO2Lazy (const (const 42)) ioTrue ioFalse
-- No output