module Control.Monad.LazyIO 
  ( LazyIO 
  , liftLazyIO
  , runLazyIO 
  ) where

import System.IO.Unsafe (unsafeInterleaveIO) 

-- | Internals guarantee work does not repeat across threads (unsafeInterleaveIO) 
newtype LazyIO a = LazyIO { forall a. LazyIO a -> IO a
unLazyIO :: IO a } 

instance Functor LazyIO where
  fmap :: forall a b. (a -> b) -> LazyIO a -> LazyIO b
fmap a -> b
f LazyIO a
io = IO b -> LazyIO b
forall a. IO a -> LazyIO a
LazyIO (IO b -> LazyIO b) -> IO b -> LazyIO b
forall a b. (a -> b) -> a -> b
$ (a -> b) -> IO a -> IO b
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> b
f (IO a -> IO a
forall a. IO a -> IO a
unsafeInterleaveIO (IO a -> IO a) -> IO a -> IO a
forall a b. (a -> b) -> a -> b
$ LazyIO a -> IO a
forall a. LazyIO a -> IO a
unLazyIO LazyIO a
io) 

instance Applicative LazyIO where
  pure :: forall a. a -> LazyIO a
pure = IO a -> LazyIO a
forall a. IO a -> LazyIO a
LazyIO (IO a -> LazyIO a) -> (a -> IO a) -> a -> LazyIO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> IO a
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure 
  LazyIO (a -> b)
f <*> :: forall a b. LazyIO (a -> b) -> LazyIO a -> LazyIO b
<*> LazyIO a
g = IO b -> LazyIO b
forall a. IO a -> LazyIO a
LazyIO (IO b -> LazyIO b) -> IO b -> LazyIO b
forall a b. (a -> b) -> a -> b
$ do
    a -> b
f' <- IO (a -> b) -> IO (a -> b)
forall a. IO a -> IO a
unsafeInterleaveIO (IO (a -> b) -> IO (a -> b)) -> IO (a -> b) -> IO (a -> b)
forall a b. (a -> b) -> a -> b
$ LazyIO (a -> b) -> IO (a -> b)
forall a. LazyIO a -> IO a
unLazyIO LazyIO (a -> b)
f 
    a
g' <- IO a -> IO a
forall a. IO a -> IO a
unsafeInterleaveIO (IO a -> IO a) -> IO a -> IO a
forall a b. (a -> b) -> a -> b
$ LazyIO a -> IO a
forall a. LazyIO a -> IO a
unLazyIO LazyIO a
g 
    b -> IO b
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (b -> IO b) -> b -> IO b
forall a b. (a -> b) -> a -> b
$ a -> b
f' a
g'  
  

-- | IO action should be commutative (order independent) 
liftLazyIO :: IO a -> LazyIO a 
liftLazyIO :: forall a. IO a -> LazyIO a
liftLazyIO = IO a -> LazyIO a
forall a. IO a -> LazyIO a
LazyIO 

runLazyIO :: LazyIO a -> IO a 
runLazyIO :: forall a. LazyIO a -> IO a
runLazyIO = IO a -> IO a
forall a. IO a -> IO a
unsafeInterleaveIO (IO a -> IO a) -> (LazyIO a -> IO a) -> LazyIO a -> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LazyIO a -> IO a
forall a. LazyIO a -> IO a
unLazyIO