module Control.Monad.EffRef
( EffRef (..)
, SafeIO (..)
, EffIORef (..)
, putStrLn_
, forkIOs
) where
import Control.Concurrent
import Control.Exception (evaluate)
import Control.Monad
import Control.Monad.Trans
import Control.Monad.Trans.Identity
import System.Directory
import System.FSNotify
import Filesystem.Path hiding (FilePath)
import Filesystem.Path.CurrentOS hiding (FilePath)
import Prelude hiding ((.), id)
import Control.Monad.Restricted
import Control.Monad.Register
import Control.Monad.ExtRef
class ExtRef m => EffRef m where
liftEffectM' :: Morph (EffectM m) m
onChange :: Eq a => Bool -> ReadRef m a -> (a -> m (m ())) -> m ()
toReceive :: Eq a => (a -> WriteRef m ()) -> ((a -> EffectM m ()) -> EffectM m (Command -> EffectM m ())) -> m (Command -> EffectM m ())
rEffect :: (EffRef m, Eq a) => Bool -> ReadRef m a -> (a -> EffectM m ()) -> m ()
instance (ExtRef m, MonadRegister m, ExtRef (EffectM m), Ref m ~ Ref (EffectM m)) => EffRef (IdentityT m) where
liftEffectM' = liftEffectM
onChange init = toSend_ init . liftReadRef
toReceive fm = toReceive_ (liftWriteRef . fm)
rEffect init r f = onChange init r $ return . liftEffectM' . f
class (EffRef m, SafeIO m, SafeIO (ReadRef m)) => EffIORef m where
asyncWrite :: Eq a => Int -> (a -> WriteRef m ()) -> a -> m ()
fileRef :: FilePath -> m (Ref m (Maybe String))
putStr_ :: String -> m ()
getLine_ :: (String -> WriteRef m ()) -> m ()
registerIO :: Eq a => (a -> WriteRef m ()) -> ((a -> IO ()) -> IO (Command -> IO ())) -> m ()
putStrLn_ :: EffIORef m => String -> m ()
putStrLn_ = putStr_ . (++ "\n")
instance (ExtRef m, MonadRegister m, ExtRef (EffectM m), Ref m ~ Ref (EffectM m), MonadIO' (EffectM m), SafeIO (ReadRef m), SafeIO m) => EffIORef (IdentityT m) where
registerIO r fm = do
_ <- toReceive r $ \x -> unliftIO $ \u -> liftM (fmap liftIO) $ liftIO $ fm $ u . x
return ()
asyncWrite t r a
= registerIO r $ \re -> forkIOs [ threadDelay t, re a ]
putStr_ = liftIO' . putStr
getLine_ w = registerIO w $ \re -> do
_ <- forkIO $ getLine >>= re
return $ const $ return ()
fileRef f = do
ms <- liftIO' r
ref <- newRef ms
v <- liftIO' newEmptyMVar
vman <- liftIO' newEmptyMVar
cf <- liftIO' $ canonicalizePath f
let
cf' = decodeString cf
g = (== cf')
h = tryPutMVar v () >> return ()
filt (Added x _) = g x
filt (Modified x _) = g x
filt (Removed x _) = g x
act (Added _ _) = h
act (Modified _ _) = h
act (Removed _ _) = h
startm = do
man <- startManager
watchDir man (directory cf') filt act
putMVar vman $ stopManager man
liftIO' startm
registerIO (writeRef ref) $ \re -> forkForever $ takeMVar v >> r >>= re
rEffect False (readRef ref) $ \x -> liftIO $ do
join $ takeMVar vman
_ <- tryTakeMVar v
w x
startm
return ref
where
r = do
b <- doesFileExist f
if b then do
xs <- readFile f
_ <- evaluate (length xs)
return (Just xs)
else return Nothing
w = maybe (doesFileExist f >>= \b -> when b (removeFile f)) (writeFile f)
liftIO' m = liftEffectM $ liftIO m
forkForever :: IO () -> IO (Command -> IO ())
forkForever = forkIOs . repeat
forkIOs :: [IO ()] -> IO (Command -> IO ())
forkIOs ios = do
x <- newMVar ()
let g [] = return ()
g (i:is) = do
() <- takeMVar x
putMVar x ()
i
g is
f i Kill = killThread i
f _ Block = takeMVar x
f _ Unblock = putMVar x ()
liftM f $ forkIO $ g ios