{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE StandaloneDeriving #-}
module Reflex.Query.Base
( QueryT (..)
, runQueryT
, mapQuery
, mapQueryResult
, dynWithQueryT
, withQueryT
) where
import Control.Applicative (liftA2)
import Control.Monad.Exception
import Control.Monad.Fix
import Control.Monad.Primitive
import Control.Monad.Reader
import Control.Monad.Ref
import Control.Monad.State.Strict
import Data.Align
import Data.Dependent.Map (DMap, DSum (..))
import qualified Data.Dependent.Map as DMap
import Data.Foldable
import Data.Functor.Compose
import Data.Functor.Misc
import Data.IntMap (IntMap)
import qualified Data.IntMap as IntMap
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Monoid ((<>))
import qualified Data.Semigroup as S
import Data.Some (Some)
import qualified Data.Some as Some
import Data.These
import Reflex.Class
import Reflex.Adjustable.Class
import Reflex.DynamicWriter.Class
import Reflex.EventWriter.Base
import Reflex.EventWriter.Class
import Reflex.Host.Class
import qualified Reflex.Patch.MapWithMove as MapWithMove
import Reflex.PerformEvent.Class
import Reflex.PostBuild.Class
import Reflex.Query.Class
import Reflex.Requester.Class
import Reflex.TriggerEvent.Class
newtype QueryT t q m a = QueryT { unQueryT :: StateT [Behavior t q] (EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m)) a }
deriving (Functor, Applicative, Monad, MonadException, MonadFix, MonadIO, MonadAtomicRef)
deriving instance MonadHold t m => MonadHold t (QueryT t q m)
deriving instance MonadSample t m => MonadSample t (QueryT t q m)
runQueryT :: (MonadFix m, Additive q, Group q, Reflex t) => QueryT t q m a -> Dynamic t (QueryResult q) -> m (a, Incremental t (AdditivePatch q))
runQueryT (QueryT a) qr = do
((r, bs), es) <- runReaderT (runEventWriterT (runStateT a mempty)) qr
return (r, unsafeBuildIncremental (foldlM (\b c -> (b <>) <$> sample c) mempty bs) (fmapCheap AdditivePatch es))
newtype QueryTLoweredResult t q v = QueryTLoweredResult (v, [Behavior t q])
getQueryTLoweredResultValue :: QueryTLoweredResult t q v -> v
getQueryTLoweredResultValue (QueryTLoweredResult (v, _)) = v
getQueryTLoweredResultWritten :: QueryTLoweredResult t q v -> [Behavior t q]
getQueryTLoweredResultWritten (QueryTLoweredResult (_, w)) = w
maskMempty :: (Eq a, Monoid a) => a -> Maybe a
maskMempty x = if x == mempty then Nothing else Just x
instance (Reflex t, MonadFix m, Group q, Additive q, Query q, Eq q, MonadHold t m, Adjustable t m) => Adjustable t (QueryT t q m) where
runWithReplace (QueryT a0) a' = do
((r0, bs0), r') <- QueryT $ lift $ runWithReplace (runStateT a0 []) $ fmapCheap (flip runStateT [] . unQueryT) a'
let sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
bs' = fmapCheap snd $ r'
bbs <- hold bs0 bs'
let patches = flip pushCheap bs' $ \newBs -> do
oldBs <- sample bbs
maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
QueryT $ modify $ (:) $ pull $ sampleBs =<< sample bbs
QueryT $ lift $ tellEvent patches
return (r0, fmapCheap fst r')
traverseIntMapWithKeyWithAdjust :: forall v v'. (IntMap.Key -> v -> QueryT t q m v') -> IntMap v -> Event t (PatchIntMap v) -> QueryT t q m (IntMap v', Event t (PatchIntMap v'))
traverseIntMapWithKeyWithAdjust f im0 im' = do
let f' :: IntMap.Key -> v -> EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m) (QueryTLoweredResult t q v')
f' k v = fmap QueryTLoweredResult $ flip runStateT [] $ unQueryT $ f k v
(result0, result') <- QueryT $ lift $ traverseIntMapWithKeyWithAdjust f' im0 im'
let liftedResult0 = IntMap.map getQueryTLoweredResultValue result0
liftedResult' = fforCheap result' $ \(PatchIntMap p) -> PatchIntMap $
IntMap.map (fmap getQueryTLoweredResultValue) p
liftedBs0 :: IntMap [Behavior t q]
liftedBs0 = IntMap.map getQueryTLoweredResultWritten result0
liftedBs' :: Event t (PatchIntMap [Behavior t q])
liftedBs' = fforCheap result' $ \(PatchIntMap p) -> PatchIntMap $
IntMap.map (fmap getQueryTLoweredResultWritten) p
sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
accumBehaviors :: forall m'. MonadHold t m'
=> IntMap [Behavior t q]
-> PatchIntMap [Behavior t q]
-> m' ( Maybe (IntMap [Behavior t q])
, Maybe (AdditivePatch q))
accumBehaviors bs0 pbs@(PatchIntMap bs') = do
let p k bs = case IntMap.lookup k bs0 of
Nothing -> case bs of
Nothing -> return mempty
Just newBs -> sampleBs newBs
Just oldBs -> case bs of
Nothing -> negateG <$> sampleBs oldBs
Just newBs -> (~~) <$> sampleBs newBs <*> sampleBs oldBs
patch <- AdditivePatch . fold <$> IntMap.traverseWithKey p bs'
return (apply pbs bs0, Just patch)
(qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors liftedBs0 liftedBs'
tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
return (liftedResult0, liftedResult')
traverseDMapWithKeyWithAdjust :: forall (k :: * -> *) v v'. (DMap.GCompare k) => (forall a. k a -> v a -> QueryT t q m (v' a)) -> DMap k v -> Event t (PatchDMap k v) -> QueryT t q m (DMap k v', Event t (PatchDMap k v'))
traverseDMapWithKeyWithAdjust f dm0 dm' = do
let f' :: forall a. k a -> v a -> EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m) (Compose (QueryTLoweredResult t q) v' a)
f' k v = fmap (Compose . QueryTLoweredResult) $ flip runStateT [] $ unQueryT $ f k v
(result0, result') <- QueryT $ lift $ traverseDMapWithKeyWithAdjust f' dm0 dm'
let liftedResult0 = mapKeyValuePairsMonotonic (\(k :=> Compose r) -> k :=> getQueryTLoweredResultValue r) result0
liftedResult' = fforCheap result' $ \(PatchDMap p) -> PatchDMap $
mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mr) -> k :=> ComposeMaybe (fmap (getQueryTLoweredResultValue . getCompose) mr)) p
liftedBs0 :: Map (Some k) [Behavior t q]
liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some.This k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
liftedBs' :: Event t (PatchMap (Some k) [Behavior t q])
liftedBs' = fforCheap result' $ \(PatchDMap p) -> PatchMap $
Map.fromDistinctAscList $ (\(k :=> ComposeMaybe mr) -> (Some.This k, fmap (getQueryTLoweredResultWritten . getCompose) mr)) <$> DMap.toList p
sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
accumBehaviors :: forall m'. MonadHold t m'
=> Map (Some k) [Behavior t q]
-> PatchMap (Some k) [Behavior t q]
-> m' ( Maybe (Map (Some k) [Behavior t q])
, Maybe (AdditivePatch q))
accumBehaviors bs0 pbs@(PatchMap bs') = do
let p k bs = case Map.lookup k bs0 of
Nothing -> case bs of
Nothing -> return Nothing
Just newBs -> maskMempty <$> sampleBs newBs
Just oldBs -> case bs of
Nothing -> maskMempty . negateG <$> sampleBs oldBs
Just newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
patch <- fold <$> Map.traverseWithKey p bs'
return (apply pbs bs0, AdditivePatch <$> patch)
(qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors liftedBs0 liftedBs'
tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
return (liftedResult0, liftedResult')
traverseDMapWithKeyWithAdjustWithMove :: forall (k :: * -> *) v v'. (DMap.GCompare k) => (forall a. k a -> v a -> QueryT t q m (v' a)) -> DMap k v -> Event t (PatchDMapWithMove k v) -> QueryT t q m (DMap k v', Event t (PatchDMapWithMove k v'))
traverseDMapWithKeyWithAdjustWithMove f dm0 dm' = do
let f' :: forall a. k a -> v a -> EventWriterT t q (ReaderT (Dynamic t (QueryResult q)) m) (Compose (QueryTLoweredResult t q) v' a)
f' k v = fmap (Compose . QueryTLoweredResult) $ flip runStateT [] $ unQueryT $ f k v
(result0, result') <- QueryT $ lift $ traverseDMapWithKeyWithAdjustWithMove f' dm0 dm'
let liftedResult0 = mapKeyValuePairsMonotonic (\(k :=> Compose r) -> k :=> getQueryTLoweredResultValue r) result0
liftedResult' = fforCheap result' $ mapPatchDMapWithMove (getQueryTLoweredResultValue . getCompose)
liftedBs0 :: Map (Some k) [Behavior t q]
liftedBs0 = Map.fromDistinctAscList $ (\(k :=> Compose r) -> (Some.This k, getQueryTLoweredResultWritten r)) <$> DMap.toList result0
liftedBs' :: Event t (PatchMapWithMove (Some k) [Behavior t q])
liftedBs' = fforCheap result' $ weakenPatchDMapWithMoveWith (getQueryTLoweredResultWritten . getCompose)
sampleBs :: forall m'. MonadSample t m' => [Behavior t q] -> m' q
sampleBs = foldlM (\b a -> (b <>) <$> sample a) mempty
accumBehaviors' :: forall m'. MonadHold t m'
=> Map (Some k) [Behavior t q]
-> PatchMapWithMove (Some k) [Behavior t q]
-> m' ( Maybe (Map (Some k) [Behavior t q])
, Maybe (AdditivePatch q))
accumBehaviors' bs0 pbs = do
let bs' = unPatchMapWithMove pbs
p k bs = case Map.lookup k bs0 of
Nothing -> case MapWithMove._nodeInfo_from bs of
MapWithMove.From_Delete -> return Nothing
MapWithMove.From_Insert newBs -> maskMempty <$> sampleBs newBs
MapWithMove.From_Move k' -> case Map.lookup k' bs0 of
Nothing -> return Nothing
Just newBs -> maskMempty <$> sampleBs newBs
Just oldBs -> case MapWithMove._nodeInfo_from bs of
MapWithMove.From_Delete -> maskMempty . negateG <$> sampleBs oldBs
MapWithMove.From_Insert newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
MapWithMove.From_Move k'
| k' == k -> return Nothing
| otherwise -> case Map.lookup k' bs0 of
Nothing -> maskMempty . negateG <$> sampleBs oldBs
Just newBs -> maskMempty <$> ((~~) <$> sampleBs newBs <*> sampleBs oldBs)
patch <- fold <$> Map.traverseWithKey p bs'
return (apply pbs bs0, AdditivePatch <$> patch)
(qpatch :: Event t (AdditivePatch q)) <- mapAccumMaybeM_ accumBehaviors' liftedBs0 liftedBs'
tellQueryIncremental $ unsafeBuildIncremental (fold <$> mapM sampleBs liftedBs0) qpatch
return (liftedResult0, liftedResult')
instance MonadTrans (QueryT t q) where
lift = QueryT . lift . lift . lift
instance PrimMonad m => PrimMonad (QueryT t q m) where
type PrimState (QueryT t q m) = PrimState m
primitive = lift . primitive
instance PostBuild t m => PostBuild t (QueryT t q m) where
getPostBuild = lift getPostBuild
instance (MonadAsyncException m) => MonadAsyncException (QueryT t q m) where
mask f = QueryT $ mask $ \unMask -> unQueryT $ f $ QueryT . unMask . unQueryT
instance TriggerEvent t m => TriggerEvent t (QueryT t q m) where
newTriggerEvent = lift newTriggerEvent
newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
instance PerformEvent t m => PerformEvent t (QueryT t q m) where
type Performable (QueryT t q m) = Performable m
performEvent_ = lift . performEvent_
performEvent = lift . performEvent
instance MonadRef m => MonadRef (QueryT t q m) where
type Ref (QueryT t q m) = Ref m
newRef = QueryT . newRef
readRef = QueryT . readRef
writeRef r = QueryT . writeRef r
instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (QueryT t q m) where
newEventWithTrigger = QueryT . newEventWithTrigger
newFanEventWithTrigger a = QueryT . lift $ newFanEventWithTrigger a
instance (Monoid a, Monad m) => Monoid (QueryT t q m a) where
mempty = pure mempty
mappend = (<>)
instance (S.Semigroup a, Monad m) => S.Semigroup (QueryT t q m a) where
(<>) = liftA2 (S.<>)
withQueryT :: (MonadFix m, PostBuild t m, Group q, Group q', Additive q, Additive q', Query q')
=> QueryMorphism q q'
-> QueryT t q m a
-> QueryT t q' m a
withQueryT f a = do
r' <- askQueryResult
(result, q) <- lift $ runQueryT a $ mapQueryResult f <$> r'
tellQueryIncremental $ unsafeBuildIncremental
(fmap (mapQuery f) (sample (currentIncremental q)))
(fmapCheap (AdditivePatch . mapQuery f . unAdditivePatch) $ updatedIncremental q)
return result
dynWithQueryT :: (MonadFix m, PostBuild t m, Group q, Additive q, Group q', Additive q', Query q')
=> Dynamic t (QueryMorphism q q')
-> QueryT t q m a
-> QueryT t q' m a
dynWithQueryT f q = do
r' <- askQueryResult
(result, q') <- lift $ runQueryT q $ zipDynWith mapQueryResult f r'
tellQueryIncremental $ zipDynIncrementalWith mapQuery f q'
return result
where zipDynIncrementalWith g da ib =
let eab = align (updated da) (updatedIncremental ib)
ec = flip push eab $ \case
This a -> do
aOld <- sample $ current da
b <- sample $ currentIncremental ib
return $ Just $ AdditivePatch (g a b ~~ g aOld b)
That (AdditivePatch b) -> do
a <- sample $ current da
return $ Just $ AdditivePatch $ g a b
These a (AdditivePatch b) -> do
aOld <- sample $ current da
bOld <- sample $ currentIncremental ib
return $ Just $ AdditivePatch $ mconcat [ g a bOld, negateG (g aOld bOld), g a b]
in unsafeBuildIncremental (g <$> sample (current da) <*> sample (currentIncremental ib)) ec
instance (Monad m, Group q, Additive q, Query q, Reflex t) => MonadQuery t q (QueryT t q m) where
tellQueryIncremental q = do
QueryT (modify (currentIncremental q:))
QueryT (lift (tellEvent (fmapCheap unAdditivePatch (updatedIncremental q))))
askQueryResult = QueryT ask
queryIncremental q = do
tellQueryIncremental q
zipDynWith crop (incrementalToDynamic q) <$> askQueryResult
instance Requester t m => Requester t (QueryT t q m) where
type Request (QueryT t q m) = Request m
type Response (QueryT t q m) = Response m
requesting = lift . requesting
requesting_ = lift . requesting_
instance EventWriter t w m => EventWriter t w (QueryT t q m) where
tellEvent = lift . tellEvent
instance DynamicWriter t w m => DynamicWriter t w (QueryT t q m) where
tellDyn = lift . tellDyn