module Control.Monad.EffRef
( EffRef (..)
, SafeIO (..)
, EffIORef (..)
, asyncWrite
, putStrLn_
, forkIOs
) where
import Control.Concurrent
import Control.Exception (evaluate)
import Control.Monad
import Control.Monad.Base
import Control.Monad.Trans.Control
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 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 ()
asyncWrite' :: Int -> WriteRef m () -> 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")
asyncWrite :: EffIORef m => Int -> (a -> WriteRef m ()) -> a -> m ()
asyncWrite t f a = asyncWrite' t $ f a
instance (ExtRef m, MonadRegister m, ExtRef (EffectM m), Ref m ~ Ref (EffectM m), MonadBaseControl IO (EffectM m), SafeIO (ReadRef m), SafeIO m) => EffIORef (IdentityT m) where
registerIO r fm = do
_ <- toReceive r $ \x -> unliftIO $ \u -> liftM (fmap liftBase) $ fm $ void . u . x
return ()
asyncWrite_ t r a
= registerIO r $ \re -> forkIOs [ threadDelay t, re a ]
asyncWrite' t r = asyncWrite_ t (const r) ()
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 _ _) = putStrLn "added" >> h
act (Modified _ _) = putStrLn "mod" >> h
act (Removed _ _) = putStrLn "rem" >> h
startm = do
putStrLn " start"
man <- startManager
putMVar vman $ putStrLn " stop" >> stopManager man
watchDir man (directory cf') filt act
liftIO' startm
registerIO (writeRef ref) $ \re -> forkForever $ takeMVar v >> r >>= re
rEffect False (readRef ref) $ \x -> liftBase $ do
join $ takeMVar vman
_ <- tryTakeMVar v
putStrLn " write"
w x
threadDelay 10000
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 $ liftBase 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