2023-04-10 19:37:08 +00:00
{- # LANGUAGE MagicHash # -}
{- # LANGUAGE LambdaCase # -}
{- # LANGUAGE UnboxedTuples # -}
2023-04-11 02:32:12 +00:00
module UIO
( module UIO
, module X
) where
2023-04-10 19:37:08 +00:00
import Control.Monad.Fix
2023-04-11 02:32:12 +00:00
import Data.Foldable
2023-04-10 19:37:08 +00:00
import Data.Semigroup
2023-04-11 02:32:12 +00:00
import GHC.IO
import GHC.Prim
2023-07-09 16:34:47 +00:00
import GHC.Magic
2023-04-11 02:32:12 +00:00
import UIO.Plugin as X
2023-04-10 19:37:08 +00:00
2023-04-10 21:41:45 +00:00
data RemainingWork = RemainingWork
2023-04-10 19:37:08 +00:00
2023-04-10 21:41:45 +00:00
instance Semigroup RemainingWork where
2023-04-10 19:37:08 +00:00
{- # INLINE (<>) # -}
( <> ) = seq
sconcat = mconcat . toList
stimes _ d = d
2023-04-10 21:41:45 +00:00
instance Monoid RemainingWork where
2023-04-10 19:37:08 +00:00
{- # INLINE mempty # -}
2023-04-10 21:41:45 +00:00
mempty = RemainingWork
2023-04-10 19:37:08 +00:00
mconcat = \ case
2023-04-10 21:41:45 +00:00
[] -> RemainingWork
2023-04-10 19:37:08 +00:00
x : xs -> x ` seq ` mconcat xs
-- Unordered IO - we want to allocate things, strictly evaluate things, etc., but we don't actually care what order it is done in
2023-04-13 01:44:43 +00:00
newtype UIO a = UIO { unUIO :: State # RealWorld -> ( RemainingWork , a ) }
2023-04-10 19:37:08 +00:00
2023-04-13 01:44:43 +00:00
instance Functor UIO where
2023-04-10 19:37:08 +00:00
{- # INLINE fmap # -}
fmap f x = x >>= ( pure . f )
2023-04-13 01:44:43 +00:00
instance Applicative UIO where
2023-04-10 19:37:08 +00:00
{- # INLINE pure # -}
{- # INLINE (*>) # -}
{- # INLINE (<*>) # -}
2023-04-13 01:44:43 +00:00
pure x = UIO ( \ _ -> ( RemainingWork , x ) )
UIO m *> UIO k = UIO ( \ s ->
2023-07-09 16:34:47 +00:00
let ( ms , _ ) = m ( uniqueState # 1 # s )
( ks , b ) = k ( uniqueState # 2 # s )
2023-04-10 21:41:45 +00:00
in ( ms <> ks , b ) )
2023-04-13 01:44:43 +00:00
UIO m <*> UIO k = UIO ( \ s ->
2023-07-09 16:34:47 +00:00
let ( ms , f ) = m ( uniqueState # 1 # s )
( ks , x ) = k ( uniqueState # 2 # s )
2023-04-10 21:41:45 +00:00
in ( ms <> ks , f x ) )
2023-04-10 19:37:08 +00:00
2023-04-13 01:44:43 +00:00
instance Monad UIO where
2023-04-10 19:37:08 +00:00
{- # INLINE (>>) # -}
{- # INLINE (>>=) # -}
( >> ) = ( *> )
2023-04-13 01:44:43 +00:00
UIO m >>= k = UIO ( \ s ->
2023-07-09 16:34:47 +00:00
let ( ms , a ) = m ( uniqueState # 1 # s )
( ks , b ) = unUIO ( k a ) ( uniqueState # 2 # s )
2023-04-10 21:41:45 +00:00
in ( ms <> ks , b ) )
2023-04-10 19:37:08 +00:00
2023-04-13 01:44:43 +00:00
instance MonadFix UIO where
2023-04-11 02:32:12 +00:00
{- # INLINE mfix # -}
2023-04-13 01:44:43 +00:00
mfix k = UIO ( \ s ->
let ( ks , result ) = unUIO ( k result ) s
2023-04-10 19:37:08 +00:00
in ( ks , result ) )
2023-04-13 01:44:43 +00:00
runUIO :: UIO a -> IO a
runUIO ( UIO m ) = do
2023-04-10 19:37:08 +00:00
-- We use a bang pattern here instead of "evaluate", because "evaluate" leaves a "seq#" clutting up our core, but the bang pattern does not
2023-07-09 16:34:47 +00:00
( ! RemainingWork , result ) <- IO ( \ s -> ( # s , m s # ) ) --TODO: This returns the same state we were given; should we call uniqueState# 1# or something on it?
2023-04-10 19:37:08 +00:00
pure result
-- The following is marked NOINLINE because unsafeDupablePerformIO is marked NOINLINE. I don't really understand it.
{- # INLINE unordered # -}
2023-04-13 01:44:43 +00:00
unordered :: IO a -> UIO a
unordered ( IO m ) = UIO ( \ s -> let x = case m s of ( # _ , x # ) -> x in ( x ` seq ` RemainingWork , x ) )
2023-04-10 21:41:45 +00:00
-- | Perform an action only when its result is needed. This action will be unique, but the computation will be considered finished regardless of whether this action has run. This is appropriate for functions like `newIORef`.
{- # INLINE timeless # -}
2023-04-13 01:44:43 +00:00
timeless :: IO a -> UIO a
timeless ( IO m ) = UIO ( \ s -> ( RemainingWork , case m s of ( # _ , x # ) -> x ) )
2023-04-10 19:37:08 +00:00
2023-07-09 16:34:47 +00:00
{- # INLINE listen # -}
listen :: UIO a -> UIO ( RemainingWork , a )
listen ( UIO m ) = UIO ( \ s -> let ( done , a ) = m s in ( done , ( done , a ) ) )
2023-04-10 21:41:45 +00:00
2023-07-09 16:34:47 +00:00
{- # INLINE after # -}
after :: RemainingWork -> UIO a -> UIO a
after w ( UIO m ) = UIO ( \ s -> m ( w ` seq ` s ) )