{-# language CPP #-}
{-# language DefaultSignatures #-}
{-# language DeriveFunctor #-}
{-# language FlexibleInstances #-}
{-# language FunctionalDependencies #-}
{-# language GADTs #-}
{-# language GeneralizedNewtypeDeriving #-}
{-# language MultiParamTypeClasses #-}
{-# language RankNTypes #-}
{-# language ScopedTypeVariables #-}
{-# language UndecidableInstances #-}
module Rock.Core where
#if MIN_VERSION_base(4,12,0)
import Protolude hiding (Ap)
#else
import Protolude
#endif
import Control.Monad.Cont
import Control.Monad.Identity
import qualified Control.Monad.RWS.Lazy as Lazy
import qualified Control.Monad.RWS.Strict as Strict
import qualified Control.Monad.State.Lazy as Lazy
import qualified Control.Monad.State.Strict as Strict
import Control.Monad.Trans.Maybe
import qualified Control.Monad.Writer.Lazy as Lazy
import qualified Control.Monad.Writer.Strict as Strict
import Data.Dependent.Map(DMap, GCompare)
import qualified Data.Dependent.Map as DMap
import Data.GADT.Compare
import Rock.Hashed
import Rock.HashTag
import Rock.Traces(Traces)
import qualified Rock.Traces as Traces
type Rules f = GenRules f f
type GenRules f g = forall a. f a -> Task g a
newtype Task f a = Task { unTask :: IO (Result f a) }
data Result f a
= Done a
| Blocked !(BlockedTask f a)
data BlockedTask f a where
BlockedTask :: Block f a -> (a -> Task f b) -> BlockedTask f b
data Block f a where
Fetch :: f a -> Block f a
Ap :: !(BlockedTask f (a -> b)) -> !(BlockedTask f a) -> Block f b
class Monad m => MonadFetch f m | m -> f where
fetch :: f a -> m a
default fetch
:: (MonadTrans t, MonadFetch f m1, m ~ t m1)
=> f a
-> m a
fetch = lift . fetch
instance MonadFetch f m => MonadFetch f (ContT r m)
instance MonadFetch f m => MonadFetch f (ExceptT e m)
instance MonadFetch f m => MonadFetch f (IdentityT m)
instance MonadFetch f m => MonadFetch f (MaybeT m)
instance MonadFetch f m => MonadFetch f (ReaderT r m)
instance (MonadFetch f m, Monoid w) => MonadFetch f (Strict.RWST r w s m)
instance (MonadFetch f m, Monoid w) => MonadFetch f (Lazy.RWST r w s m)
instance MonadFetch f m => MonadFetch f (Strict.StateT s m)
instance MonadFetch f m => MonadFetch f (Lazy.StateT s m)
instance (Monoid w, MonadFetch f m) => MonadFetch f (Strict.WriterT w m)
instance (Monoid w, MonadFetch f m) => MonadFetch f (Lazy.WriterT w m)
instance Functor (Task f) where
{-# INLINE fmap #-}
fmap f (Task t) = Task $ fmap f <$> t
instance Applicative (Task f) where
{-# INLINE pure #-}
pure = Task . pure . Done
{-# INLINE (<*>) #-}
Task mrf <*> Task mrx = Task $ (<*>) <$> mrf <*> mrx
instance Monad (Task f) where
{-# INLINE (>>) #-}
(>>) = (*>)
{-# INLINE (>>=) #-}
Task ma >>= f = Task $ do
ra <- ma
case ra of
Done a -> unTask $ f a
Blocked (BlockedTask b k) -> return $ Blocked $ BlockedTask b $ k >=> f
instance MonadIO (Task f) where
{-# INLINE liftIO #-}
liftIO io = Task $ pure <$> io
instance MonadFetch f (Task f) where
fetch key = Task $ pure $ Blocked $ BlockedTask (Fetch key) pure
instance Functor (Result f) where
{-# INLINE fmap #-}
fmap f (Done x) = Done $ f x
fmap f (Blocked b) = Blocked $ f <$> b
instance Applicative (Result f) where
{-# INLINE pure #-}
pure = Done
{-# INLINE (<*>) #-}
Done f <*> Done x = Done $ f x
Done f <*> Blocked b = Blocked $ f <$> b
Blocked b <*> Done x = Blocked $ ($ x) <$> b
Blocked b1 <*> Blocked b2 = Blocked $ BlockedTask (Ap b1 b2) pure
instance Monad (Result f) where
{-# INLINE (>>) #-}
(>>) = (*>)
{-# INLINE (>>=) #-}
Done x >>= f = f x
Blocked (BlockedTask b t) >>= f = Blocked $ BlockedTask b $ t >=> Task . pure . f
instance Functor (BlockedTask f) where
{-# INLINE fmap #-}
fmap f (BlockedTask b t) = BlockedTask b $ fmap f <$> t
transFetch
:: (forall b. f b -> Task f' b)
-> Task f a
-> Task f' a
transFetch f task = Task $ do
result <- unTask task
case result of
Done a -> return $ Done a
Blocked b -> unTask $ transFetchBlockedTask f b
transFetchBlockedTask
:: (forall b. f b -> Task f' b)
-> BlockedTask f a
-> Task f' a
transFetchBlockedTask f (BlockedTask b t) = do
a <- transFetchBlock f b
transFetch f $ t a
transFetchBlock
:: (forall b. f b -> Task f' b)
-> Block f a
-> Task f' a
transFetchBlock f (Fetch k) = f k
transFetchBlock f (Ap b1 b2) = transFetchBlockedTask f b1 <*> transFetchBlockedTask f b2
type Strategy = forall a b. IO (a -> b) -> IO a -> IO b
sequentially :: Strategy
sequentially = (<*>)
inParallel :: Strategy
inParallel mf mx = withAsync mf $ \af -> do
x <- mx
f <- wait af
return $ f x
newtype Sequential m a = Sequential { runSequential :: m a }
deriving (Functor, Monad, MonadIO, MonadFetch f)
instance Monad m => Applicative (Sequential m) where
{-# INLINE pure #-}
pure = Sequential . return
{-# INLINE (<*>) #-}
Sequential mf <*> Sequential mx = Sequential $ mf >>= \f -> fmap f mx
runTask :: Strategy -> Rules f -> Task f a -> IO a
runTask strategy rules task = do
result <- unTask task
case result of
Done a -> return a
Blocked b -> runBlockedTask strategy rules b
runBlockedTask :: Strategy -> Rules f -> BlockedTask f a -> IO a
runBlockedTask strategy rules (BlockedTask b f) = do
a <- runBlock strategy rules b
runTask strategy rules $ f a
runBlock :: Strategy -> Rules f -> Block f a -> IO a
runBlock strategy rules (Fetch key) =
runTask strategy rules $ rules key
runBlock strategy rules (Ap bf bx) =
strategy (runBlockedTask strategy rules bf) (runBlockedTask strategy rules bx)
track :: forall f a. GCompare f => Task f a -> Task f (a, DMap f Identity)
track task = do
depsVar <- liftIO $ newMVar mempty
let
record :: f b -> Task f b
record key = do
value <- fetch key
liftIO $ modifyMVar_ depsVar $ pure . DMap.insert key (Identity value)
return value
result <- transFetch record task
deps <- liftIO $ readMVar depsVar
return (result, deps)
memoise
:: forall f g
. GCompare f
=> MVar (DMap f MVar)
-> GenRules f g
-> GenRules f g
memoise startedVar rules (key :: f a) =
join $ liftIO $ modifyMVar startedVar $ \started ->
case DMap.lookup key started of
Nothing -> do
valueVar <- newEmptyMVar
return
( DMap.insert key valueVar started
, do
value <- rules key
liftIO $ putMVar valueVar value
return value
)
Just valueVar ->
return (started, liftIO $ readMVar valueVar)
verifyTraces
:: (GCompare f, HashTag f)
=> MVar (Traces f)
-> GenRules (Writer TaskKind f) f
-> Rules f
verifyTraces tracesVar rules key = do
traces <- liftIO $ readMVar tracesVar
maybeValue <- case DMap.lookup key traces of
Nothing -> return Nothing
Just oldValueDeps ->
Traces.verifyDependencies fetchHashed oldValueDeps
case maybeValue of
Nothing -> do
((value, taskKind), deps) <- track $ rules $ Writer key
case taskKind of
Input ->
return ()
NonInput ->
liftIO $ modifyMVar_ tracesVar
$ pure
. Traces.record key value deps
return value
Just value -> return value
where
fetchHashed :: HashTag f => f a -> Task f (Hashed a)
fetchHashed key' = hashed key' <$> fetch key'
data TaskKind
= Input
| NonInput
data Writer w f a where
Writer :: f a -> Writer w f (a, w)
instance GEq f => GEq (Writer w f) where
geq (Writer f) (Writer g) = case geq f g of
Nothing -> Nothing
Just Refl -> Just Refl
instance GCompare f => GCompare (Writer w f) where
gcompare (Writer f) (Writer g) = case gcompare f g of
GLT -> GLT
GEQ -> GEQ
GGT -> GGT
writer
:: forall f w g
. (forall a. f a -> w -> Task g ())
-> GenRules (Writer w f) g
-> GenRules f g
writer write rules key = do
(res, w) <- rules $ Writer key
write key w
return res
traceFetch
:: (forall a. f a -> Task g ())
-> (forall a. f a -> a -> Task g ())
-> GenRules f g
-> GenRules f g
traceFetch before after rules key = do
before key
result <- rules key
after key result
return result