packages feed

immutaball-core-0.1.0.4.1: Immutaball/Share/ImmutaballIO/BasicIO.hs

{-# OPTIONS_GHC -fno-warn-tabs #-}  -- Support tab indentation better, for a better default of no warning if tabs are used: https://dmitryfrank.com/articles/indent_with_tabs_align_with_spaces .
-- Enable warnings:
{-# OPTIONS_GHC -Wall -fno-warn-tabs #-}

-- ImmutaballIO/Basic.hs.

{-# LANGUAGE Haskell2010 #-}
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, InstanceSigs, ScopedTypeVariables, ExistentialQuantification #-}

module Immutaball.Share.ImmutaballIO.BasicIO
	(
		-- * BasicIO
		BasicIO,
		BasicIOF(..),
		runBasicIO,
		(-<>>),

		-- * mfix
		FixBasicIOException(..),
		fixBasicIOExceptionToException,
		fixBasicIOExceptionFromException,
		PrematureEvaluationFixBasicIOException(..),
		EmptyFixBasicIOException(..),
		fixBasicIOF,
		unsafeFixBasicIOFTo,

		-- * Runners
		runBasicIOIO,
		runDirectoryBasicIO,
		runSDLBasicIO,
		runGLBasicIO,
		hReadBytesSync,
		hReadTextSync,

		-- * BasicIO aliases that apply the Fixed wrapper
		mkEmptyBasicIO,
		mkPureBasicIO,
		mkUnfixBasicIO,
		mkJoinBasicIO,
		mkAndBasicIO,
		mkThenBasicIO,
		mkExitSuccessBasicIO,
		mkExitFailureBasicIO,
		mkDirectoryIO,
		mkGetArgs,
		mkGetArgsSync,
		mkGetEnvironment,
		mkGetEnvironmentSync,
		mkPutStrLn,
		mkGetContents,
		mkGetContentsSync,
		mkDoesPathExist,
		mkDoesPathExistSync,
		mkWriteBytes,
		mkWriteText,
		mkReadBytes,
		mkReadBytesSync,
		mkReadText,
		mkReadTextSync,
		mkCreateDirectoryIfMissing,
		mkGetDirectoryContents,
		mkGetDirectoryContentsSync,
		mkForkIO,
		mkForkOS,
		mkSDLIO,
		mkGLIO,
		mkDelayUs,
		mkGetUs,

		-- * Utils
		getUsIO
	) where

import Prelude ()
import Immutaball.Prelude

import Control.Concurrent
--import Control.Exception (catch, throwIO)
import Control.Monad
import Control.Monad.Fix
import Data.Time.Clock.System
import System.Environment
import System.Exit

import Control.Concurrent.Async
import Control.Concurrent.Thread.Delay
import Control.Parallel
import qualified Data.ByteString.Lazy as BL
import qualified Data.Text as T
import qualified Data.Text.IO.Utf8 as TIO
import System.Directory

import Immutaball.Share.ImmutaballIO.DirectoryIO
import Immutaball.Share.ImmutaballIO.GLIO
import Immutaball.Share.ImmutaballIO.SDLIO
import Immutaball.Share.Utils

-- (mfix imports.)
--import Control.Concurrent.MVar
import Control.Exception
import Data.Typeable
import GHC.IO.Unsafe (unsafeDupableInterleaveIO)
import System.IO.Unsafe (unsafePerformIO)

-- * BasicIO

type BasicIO = Fixed BasicIOF
data BasicIOF me =
	  EmptyBasicIOF
	| PureBasicIOF me
	| UnfixBasicIOF (BasicIOF me)
	| JoinBasicIOF (BasicIOF (BasicIOF me))
	| AndBasicIOF me me
	| ThenBasicIOF me me
	| ExitSuccessBasicIOF
	| ExitFailureBasicIOF

	| DirectoryIO (DirectoryIOF me)

	| GetArgs (Async [String] -> me)
	| GetArgsSync ([String] -> me)
	| GetEnvironment (Async [(String, String)] -> me)
	| GetEnvironmentSync ([(String, String)] -> me)
	| PutStrLn String me
	| GetContents (Async String -> me)
	| GetContentsSync (String -> me)

	| DoesPathExist FilePath (Async Bool -> me)
	| DoesPathExistSync FilePath (Bool -> me)
	| WriteBytes FilePath BL.ByteString me
	| WriteText FilePath T.Text me
	-- | Optional error handler.
	| ReadBytes FilePath (Async (Either IOException BL.ByteString) -> me)
	| ReadBytesSync FilePath (Either IOException BL.ByteString -> me)
	-- | Optional error handler.
	| ReadText FilePath (Async (Either IOException T.Text) -> me)
	| ReadTextSync FilePath (Either IOException T.Text -> me)
	| CreateDirectoryIfMissing FilePath me
	| GetDirectoryContents FilePath (Async [FilePath] -> me)
	| GetDirectoryContentsSync FilePath ([FilePath] -> me)
	| ForkIO me me
	| ForkOS me me

	| SDLIO (SDLIOF me)
	| GLIO (GLIOF me)

	| DelayUs Integer me
	| GetUs (Integer -> me)

runBasicIO :: BasicIO -> IO ()
runBasicIO bio = cata runBasicIOIO bio

instance Semigroup (BasicIOF BasicIO) where
	a <> b = Fixed a `AndBasicIOF` Fixed b
instance Monoid (BasicIOF BasicIO) where
	mempty = EmptyBasicIOF

instance Semigroup BasicIO where
	(Fixed a) <> (Fixed b) = Fixed (a <> b)
instance Monoid BasicIO where
	mempty = Fixed mempty

instance Functor BasicIOF where
	fmap :: (a -> b) -> (BasicIOF a -> BasicIOF b)
	fmap _f (EmptyBasicIOF)       = EmptyBasicIOF
	fmap  f (PureBasicIOF a)      = PureBasicIOF (f a)
	fmap  f (UnfixBasicIOF bio)   = UnfixBasicIOF (f <$> bio)
	fmap  f (JoinBasicIOF bio)    = JoinBasicIOF (fmap f <$> bio)
	fmap  f (AndBasicIOF a b)     = AndBasicIOF (f a) (f b)
	fmap  f (ThenBasicIOF a b)    = ThenBasicIOF (f a) (f b)
	fmap _f (ExitFailureBasicIOF) = ExitFailureBasicIOF
	fmap _f (ExitSuccessBasicIOF) = ExitSuccessBasicIOF

	fmap  f (DirectoryIO dio) = DirectoryIO (f <$> dio)

	fmap  f (GetArgs withArgs_)                  = GetArgs (f . withArgs_)
	fmap  f (GetArgsSync withArgs_)              = GetArgsSync (f . withArgs_)
	fmap  f (GetEnvironment withEnvironment)     = GetEnvironment (f . withEnvironment)
	fmap  f (GetEnvironmentSync withEnvironment) = GetEnvironmentSync (f . withEnvironment)
	fmap  f (PutStrLn str withUnit)              = PutStrLn str (f withUnit)
	fmap  f (GetContents withContents)           = GetContents (f . withContents)
	fmap  f (GetContentsSync withContents)       = GetContentsSync (f . withContents)

	fmap  f (DoesPathExist path withExists)             = DoesPathExist path (f .  withExists)
	fmap  f (DoesPathExistSync path withExists)         = DoesPathExistSync path (f .  withExists)
	fmap  f (WriteBytes path contents withUnit)         = WriteBytes path contents (f withUnit)
	fmap  f (WriteText path contents withUnit)          = WriteText path contents (f withUnit)
	fmap  f (ReadBytes path withContents)               = ReadBytes path (f . withContents)
	fmap  f (ReadBytesSync path withContents)           = ReadBytesSync path (f . withContents)
	fmap  f (ReadText path withContents)                = ReadText path (f . withContents)
	fmap  f (ReadTextSync path withContents)            = ReadTextSync path (f . withContents)
	fmap  f (CreateDirectoryIfMissing path withUnit)    = CreateDirectoryIfMissing path (f withUnit)
	fmap  f (GetDirectoryContents path withEntries)     = GetDirectoryContents path (f . withEntries)
	fmap  f (GetDirectoryContentsSync path withEntries) = GetDirectoryContentsSync path (f . withEntries)
	fmap  f (ForkIO bio withUnit)                       = ForkIO (f bio) (f withUnit)
	fmap  f (ForkOS bio withUnit)                       = ForkOS (f bio) (f withUnit)

	fmap  f (SDLIO sdlio) = SDLIO (f <$> sdlio)
	fmap  f (GLIO glio)   = GLIO  (f <$> glio)

	fmap  f (DelayUs us withUnit) = DelayUs us (f withUnit)
	fmap  f (GetUs withUs)        = GetUs (f . withUs)

instance Applicative BasicIOF where
	pure = PureBasicIOF
	mf <*> ma = JoinBasicIOF . flip fmap mf $ \f -> JoinBasicIOF .  flip fmap ma $ \a -> pure (f a)
instance Monad BasicIOF where
	return = pure
	m >>= f = JoinBasicIOF $ f <$> m
instance MonadFix BasicIOF where
	mfix :: (a -> BasicIOF a) -> BasicIOF a
	mfix = fixBasicIOF

{-
instance Foldable BasicIOF where
	-- TODO
instance Traversable BasicIOF where
	-- TODO
-}

-- | Add an ordering constraint.
infixr 6 -<>>
(-<>>) :: BasicIO -> BasicIO -> BasicIO
(-<>>) = mkThenBasicIO

-- * mfix

data FixBasicIOException = forall e. Exception e => FixBasicIOException e
instance Show FixBasicIOException where
	show (FixBasicIOException e) = show e
instance Exception FixBasicIOException
fixBasicIOExceptionToException :: Exception e => e -> SomeException
fixBasicIOExceptionToException = toException . FixBasicIOException
fixBasicIOExceptionFromException :: Exception e => SomeException -> Maybe e
fixBasicIOExceptionFromException x = do
	FixBasicIOException a <- fromException x
	cast a

data PrematureEvaluationFixBasicIOException = PrematureEvaluationFixBasicIOException
	deriving (Show)
instance Exception PrematureEvaluationFixBasicIOException where
	toException = fixBasicIOExceptionToException
	fromException = fixBasicIOExceptionFromException

data EmptyFixBasicIOException = EmptyFixBasicIOException
	deriving (Show)
instance Exception EmptyFixBasicIOException where
	toException = fixBasicIOExceptionToException
	fromException = fixBasicIOExceptionFromException

--    mfix f = mfix f >>= f
-- => mfix f = join $ f <$> mfix f
-- Incorrect: runs f twice.
	--x -> f undefined >>= mfix f
{-
fixBasicIOF :: (me -> BasicIOF me) -> BasicIOF me
fixBasicIOF f = case f (error "Error: fixBasicIOF: premature evaluation of result before we could start it!") of
	x -> joinBasicIOF $ f <$> x
-}
-- Do it like fixIO and fixST (see also their notes; it's a little tricky).
-- Use a lazily read MVar.
fixBasicIOF :: (me -> BasicIOF me) -> BasicIOF me
fixBasicIOF f = unsafePerformIO $ do
	mme <- newEmptyMVar
	return $ unsafeFixBasicIOFTo mme f

-- | Helper for fixBasicIOF.
unsafeFixBasicIOFTo :: MVar me -> (me -> BasicIOF me) -> BasicIOF me
unsafeFixBasicIOFTo mme f = unsafePerformIO $ do
	me_ <- unsafeDupableInterleaveIO (readMVar mme `catch` \BlockedIndefinitelyOnMVar -> throwIO PrematureEvaluationFixBasicIOException)
	case f me_ of
		--_y@(EmptyBasicIOF)       -> throwIO EmptyFixBasicIOException
		_y@(EmptyBasicIOF)       -> return $ EmptyBasicIOF
		y@( PureBasicIOF a)      -> putMVar mme a >> return y
		_y@(UnfixBasicIOF bio)   -> return . UnfixBasicIOF . unsafeFixBasicIOFTo mme $ const bio
		-- Join: Cover all multi-branching (or else we could hang on multiple putMVars), then just fmap for all other cases.
		_y@(JoinBasicIOF (AndBasicIOF a b)) -> return (JoinBasicIOF (AndBasicIOF (unsafeFixBasicIOFTo mme (const a)) (JoinBasicIOF $ f <$> b)))
		_y@(JoinBasicIOF (ThenBasicIOF a b)) -> return (JoinBasicIOF (ThenBasicIOF (unsafeFixBasicIOFTo mme (const a)) (JoinBasicIOF $ f <$> b)))
		_y@(JoinBasicIOF ibio)    -> return $ JoinBasicIOF (unsafeFixBasicIOFTo mme . const <$> ibio)
		_y@(AndBasicIOF  a b)    -> putMVar mme a >> return (JoinBasicIOF $ AndBasicIOF (PureBasicIOF a) (f b))
		_y@(ThenBasicIOF a b)    -> putMVar mme a >> return (JoinBasicIOF $ ThenBasicIOF (PureBasicIOF a) (f b))
		--_y@(ExitSuccessBasicIOF) -> throwIO EmptyFixBasicIOException
		--_y@(ExitFailureBasicIOF) -> throwIO EmptyFixBasicIOException
		_y@(ExitSuccessBasicIOF) -> return $ ExitSuccessBasicIOF
		_y@(ExitFailureBasicIOF) -> return $ ExitFailureBasicIOF

		_y@(DirectoryIO dio) -> return . DirectoryIO . unsafeFixDirectoryIOFTo mme $ const dio

		_y@(GetArgs            withArgs_)       -> return $ GetArgs            ((\me -> unsafePerformIO $ putMVar mme me >> return me) . withArgs_)
		_y@(GetArgsSync        withArgs_)       -> return $ GetArgsSync        ((\me -> unsafePerformIO $ putMVar mme me >> return me) . withArgs_)
		_y@(GetEnvironment     withEnvironment) -> return $ GetEnvironment     ((\me -> unsafePerformIO $ putMVar mme me >> return me) . withEnvironment)
		_y@(GetEnvironmentSync withEnvironment) -> return $ GetEnvironmentSync ((\me -> unsafePerformIO $ putMVar mme me >> return me) . withEnvironment)
		y@( PutStrLn           _str me)         -> putMVar mme me >> return y
		_y@(GetContents        withContents)    -> return $ GetContents        ((\me -> unsafePerformIO $ putMVar mme me >> return me) . withContents)
		_y@(GetContentsSync    withContents)    -> return $ GetContentsSync    ((\me -> unsafePerformIO $ putMVar mme me >> return me) . withContents)

		_y@(DoesPathExist     path withExists)            -> return $ DoesPathExist     path ((\me -> unsafePerformIO $ putMVar mme me >> return me) . withExists)
		_y@(DoesPathExistSync path withExists)            -> return $ DoesPathExistSync path ((\me -> unsafePerformIO $ putMVar mme me >> return me) . withExists)
		y@( WriteBytes        _path _contents me)         -> putMVar mme me >> return y
		y@( WriteText         _path _contents me)         -> putMVar mme me >> return y
		_y@(ReadBytes         path withContents)          -> return $ ReadBytes         path ((\me -> unsafePerformIO $ putMVar mme me >> return me) . withContents)
		_y@(ReadBytesSync     path withContents)          -> return $ ReadBytesSync     path ((\me -> unsafePerformIO $ putMVar mme me >> return me) . withContents)
		_y@(ReadText          path withContents)          -> return $ ReadText          path ((\me -> unsafePerformIO $ putMVar mme me >> return me) . withContents)
		_y@(ReadTextSync      path withContents)          -> return $ ReadTextSync      path ((\me -> unsafePerformIO $ putMVar mme me >> return me) . withContents)
		y@( CreateDirectoryIfMissing _path me)            -> putMVar mme me >> return y
		_y@(GetDirectoryContents path withEntries)        -> return $ GetDirectoryContents path ((\me -> unsafePerformIO $ putMVar mme me >> return me) . withEntries)
		_y@(GetDirectoryContentsSync path withEntries)    -> return $ GetDirectoryContentsSync path ((\me -> unsafePerformIO $ putMVar mme me >> return me) . withEntries)
		--y@( ForkIO            _bio me)                    -> putMVar mme me >> return y
		_y@(ForkIO            bio me)                     -> putMVar mme me >> return (JoinBasicIOF $ ForkIO (f bio) (PureBasicIOF me))
		--y@( ForkOS            _os me)                     -> putMVar mme me >> return y
		_y@(ForkOS            os me)                      -> putMVar mme me >> return (JoinBasicIOF $ ForkOS (f os)  (PureBasicIOF me))

		_y@(SDLIO sdlio) -> return . SDLIO . unsafeFixSDLIOFTo mme $ const sdlio
		_y@(GLIO  glio)  -> return . GLIO  . unsafeFixGLIOFTo  mme $ const glio

		y@( DelayUs _us me) -> putMVar mme me >> return y
		_y@(GetUs   withUs) -> return $ GetUs ((\me -> unsafePerformIO $ putMVar mme me >> return me) . withUs)

-- * Runners

runBasicIOIO :: BasicIOF (IO ()) -> IO ()
runBasicIOIO (EmptyBasicIOF)                          = return ()
runBasicIOIO (PureBasicIOF bio)                       = bio
runBasicIOIO (UnfixBasicIOF bio)                      = runBasicIOIO bio
runBasicIOIO (JoinBasicIOF bio)                       = runBasicIOIO $ runBasicIOIO <$> bio
runBasicIOIO (AndBasicIOF a b)                        = a `par` b `par` concurrently_ a b
runBasicIOIO (ThenBasicIOF a b)                       = a >> b
runBasicIOIO (ExitSuccessBasicIOF)                    = exitSuccess
runBasicIOIO (ExitFailureBasicIOF)                    = exitFailure
runBasicIOIO (DirectoryIO dio)                        = runDirectoryIOIO $ dio
runBasicIOIO (GetArgs withArgs_)                      = withAsync getArgs withArgs_
runBasicIOIO (GetArgsSync withArgs_)                  = getArgs >>= withArgs_
runBasicIOIO (GetEnvironment withEnvironment)         = withAsync getEnvironment withEnvironment
runBasicIOIO (GetEnvironmentSync withEnvironment)     = getEnvironment >>= withEnvironment
runBasicIOIO (PutStrLn str withUnit)                  = putStrLn str >> withUnit
runBasicIOIO (GetContents withContents)               = withAsync getContents withContents
runBasicIOIO (GetContentsSync withContents)           = getContents >>= withContents
runBasicIOIO (DoesPathExist path withExists)          = withAsync (doesPathExist path) withExists
runBasicIOIO (DoesPathExistSync path withExists)      = doesPathExist path >>= withExists
runBasicIOIO (WriteBytes path contents withUnit)      = BL.writeFile path contents >> withUnit
runBasicIOIO (WriteText path contents withUnit)       = TIO.writeFile path contents >> withUnit
runBasicIOIO (ReadBytes path withContents)            = withAsync (hReadBytesSync path) withContents
runBasicIOIO (ReadBytesSync path withContents)        = hReadBytesSync path >>= withContents
runBasicIOIO (ReadText path withContents)             = withAsync (hReadTextSync path) withContents
runBasicIOIO (ReadTextSync path withContents)         = hReadTextSync path >>= withContents
runBasicIOIO (CreateDirectoryIfMissing path withUnit) = createDirectoryIfMissing True path >> withUnit
runBasicIOIO (GetDirectoryContents path withEntries)  = withAsync (getDirectoryContents path) withEntries
runBasicIOIO (GetDirectoryContentsSync path withEntries) = getDirectoryContents path >>= withEntries
runBasicIOIO (ForkIO bio withUnit)                    = (void . forkIO) bio >> withUnit
runBasicIOIO (ForkOS bio withUnit)                    = (void . forkOS) bio >> withUnit
runBasicIOIO (SDLIO sdlio)                            = runSDLIOIO $ sdlio
runBasicIOIO (GLIO  glio)                             = runGLIOIO  $ glio
runBasicIOIO (DelayUs us withUnit)                    = delay us >> withUnit
runBasicIOIO (GetUs withUs)                           = getUsIO >>= withUs

runDirectoryBasicIO :: DirectoryIO -> BasicIO
runDirectoryBasicIO dio = Fixed . DirectoryIO $ runDirectoryBasicIO <$> getFixed dio

runSDLBasicIO :: SDLIO -> BasicIO
runSDLBasicIO sdlio = Fixed . SDLIO $ runSDLBasicIO <$> getFixed sdlio

runGLBasicIO :: GLIO -> BasicIO
runGLBasicIO glio = Fixed . GLIO $ runGLBasicIO <$> getFixed glio

hReadBytesSync :: FilePath -> IO (Either IOException BL.ByteString)
hReadBytesSync path = try $ BL.readFile path

hReadTextSync :: FilePath -> IO (Either IOException T.Text)
hReadTextSync path = try $ TIO.readFile path

-- * ImutaballIO aliases that apply the Fixed wrapper

mkEmptyBasicIO :: BasicIO
mkEmptyBasicIO = Fixed $ EmptyBasicIOF

mkPureBasicIO :: BasicIO -> BasicIO
mkPureBasicIO bio = Fixed $ PureBasicIOF bio

mkUnfixBasicIO :: BasicIO -> BasicIO
mkUnfixBasicIO bio = Fixed $ UnfixBasicIOF (getFixed bio)

mkJoinBasicIO :: BasicIO -> BasicIO
mkJoinBasicIO bio = Fixed $ JoinBasicIOF (getFixed <$> getFixed bio)

mkAndBasicIO :: BasicIO -> BasicIO -> BasicIO
mkAndBasicIO a b = Fixed $ AndBasicIOF a b

mkThenBasicIO :: BasicIO -> BasicIO -> BasicIO
mkThenBasicIO a b = Fixed $ ThenBasicIOF a b

mkExitSuccessBasicIO :: BasicIO
mkExitSuccessBasicIO = Fixed $ ExitFailureBasicIOF

mkExitFailureBasicIO :: BasicIO
mkExitFailureBasicIO = Fixed $ ExitFailureBasicIOF

mkDirectoryIO :: DirectoryIOF BasicIO -> BasicIO
mkDirectoryIO dio = Fixed $ DirectoryIO dio

mkGetArgs :: (Async [String] -> BasicIO) -> BasicIO
mkGetArgs withArgs_ = Fixed $ GetArgs withArgs_

mkGetArgsSync :: ([String] -> BasicIO) -> BasicIO
mkGetArgsSync withArgs_ = Fixed $ GetArgsSync withArgs_

mkGetEnvironment :: (Async [(String, String)] -> BasicIO) -> BasicIO
mkGetEnvironment withEnvironment = Fixed $ GetEnvironment withEnvironment

mkGetEnvironmentSync :: ([(String, String)] -> BasicIO) -> BasicIO
mkGetEnvironmentSync withEnvironment = Fixed $ GetEnvironmentSync withEnvironment

mkPutStrLn :: String -> BasicIO -> BasicIO
mkPutStrLn str bio = Fixed $ PutStrLn str bio

mkGetContents :: (Async String -> BasicIO) -> BasicIO
mkGetContents withContents = Fixed $ GetContents withContents

mkGetContentsSync :: (String -> BasicIO) -> BasicIO
mkGetContentsSync withContents = Fixed $ GetContentsSync withContents

mkDoesPathExist :: FilePath -> (Async Bool -> BasicIO) -> BasicIO
mkDoesPathExist path withExists = Fixed $ DoesPathExist path withExists

mkDoesPathExistSync :: FilePath -> (Bool -> BasicIO) -> BasicIO
mkDoesPathExistSync path withExists = Fixed $ DoesPathExistSync path withExists

mkWriteBytes :: FilePath -> BL.ByteString -> BasicIO -> BasicIO
mkWriteBytes path contents withUnit = Fixed $ WriteBytes path contents withUnit

mkWriteText :: FilePath -> T.Text -> BasicIO -> BasicIO
mkWriteText path contents withUnit = Fixed $ WriteText path contents withUnit

mkReadBytes :: FilePath -> (Async (Either IOException BL.ByteString) -> BasicIO) -> BasicIO
mkReadBytes path withContents = Fixed $ ReadBytes path withContents

mkReadBytesSync :: FilePath -> (Either IOException BL.ByteString -> BasicIO) -> BasicIO
mkReadBytesSync path withContents = Fixed $ ReadBytesSync path withContents

mkReadText :: FilePath -> (Async (Either IOException T.Text) -> BasicIO) -> BasicIO
mkReadText path withContents = Fixed $ ReadText path withContents

mkReadTextSync :: FilePath -> (Either IOException T.Text -> BasicIO) -> BasicIO
mkReadTextSync path withContents = Fixed $ ReadTextSync path withContents

mkCreateDirectoryIfMissing :: FilePath -> BasicIO -> BasicIO
mkCreateDirectoryIfMissing path withUnit = Fixed $ CreateDirectoryIfMissing path withUnit

mkGetDirectoryContents :: FilePath -> (Async [FilePath] -> BasicIO) -> BasicIO
mkGetDirectoryContents path withEntries = Fixed $ GetDirectoryContents path withEntries

mkGetDirectoryContentsSync :: FilePath -> ([FilePath] -> BasicIO) -> BasicIO
mkGetDirectoryContentsSync path withEntries = Fixed $ GetDirectoryContentsSync path withEntries

mkForkIO :: BasicIO -> BasicIO -> BasicIO
mkForkIO bio withUnit = Fixed $ ForkIO bio withUnit

mkForkOS :: BasicIO -> BasicIO -> BasicIO
mkForkOS bio withUnit = Fixed $ ForkOS bio withUnit

mkSDLIO :: SDLIOF BasicIO -> BasicIO
mkSDLIO sdlio = Fixed $ SDLIO sdlio

mkGLIO :: GLIOF BasicIO -> BasicIO
mkGLIO glio = Fixed $ GLIO glio

-- | Microseconds thread delay, with ‘unbounded-delays’.
mkDelayUs :: Integer -> BasicIO -> BasicIO
mkDelayUs us withUnit = Fixed $ DelayUs us withUnit

mkGetUs :: (Integer -> BasicIO) -> BasicIO
mkGetUs withUs = Fixed $ GetUs withUs

-- * Utils

getUsIO :: IO Integer
getUsIO = getSystemTime >>= \(MkSystemTime secs nanosecs) -> return $ (1000000 * (fromIntegral secs)) + ((fromIntegral nanosecs) `div` 1000)