-- | Module supporting the implementation of frameworks. You should import this if you -- want to build your own framework, along with one of the base MonadSwitch classes: -- -- Reflex.Monad.App for MonadIO based frameworks -- or Reflex.Monad.ReflexM for pure frameworks -- module Reflex.Monad ( module Reflex.Monad.Class , holdM , collectMapM , collectM , collect , Flow (..) , flowM , Chain (..) , chainM , (>->) , loopM ) where import Control.Applicative import Control.Monad import Control.Lens import Data.Monoid import Data.List import Data.Functor import Reflex.Monad.Class import Reflex.Monad.ReflexM import Data.Map.Strict (Map) import qualified Data.Map.Strict as Map import Prelude holdM :: (MonadSwitch t m) => m a -> Event t (m a) -> m (Dynamic t a) holdM initial e = holdDyn' =<< switchM (Updated initial e) withIds :: (MonadReflex t m) => [a] -> Event t [a] -> m (Map Int a, Event t (Map Int a)) withIds initial added = do total <- current <$> foldDyn (+) (genericLength initial) (genericLength <$> added) return (zipFrom 0 initial, attachWith zipFrom total added) where zipFrom n = Map.fromList . zip [n..] collect :: (MonadReflex t m) => [(a, Event t ())] -> Event t [(a, Event t ())] -> m (UpdatedMap t Int a) collect initial added = runReflexM $ collectM (pure <$> initial) (fmap pure <$> added) collectM :: (MonadSwitch t m) => [m (a, Event t ())] -> Event t [m (a, Event t ())] -> m (UpdatedMap t Int a) collectM initial added = do (initialMap, addedMap) <- withIds initial added rec (values, remove) <- fmap split <$> switchMapM $ UpdatedMap initialMap $ mergeWith (<>) [ fmap Just <$> addedMap, toRemove ] toRemove <- switchMerge' $ makeRemovals remove return values where makeRemovals = imap (\k -> fmap $ const $ Map.singleton k Nothing) collectMapM :: (MonadSwitch t m, Ord k) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Dynamic t (Map k a)) collectMapM input childView = do inputViews <- mapDyn (Map.mapWithKey itemView) input let updates = shallowDiff (current inputViews) (updated inputViews) initial <- sample (current inputViews) holdMapDyn =<< switchMapM (UpdatedMap initial updates) where itemView k v = holdDyn v (fmapMaybe (Map.lookup k) (updated input)) >>= childView k newtype Flow t m a = Flow { unFlow :: m (a, Event t (Flow t m a)) } flowM :: (MonadSwitch t m) => Flow t m a -> m (Dynamic t a) flowM (Flow w) = do rec result <- holdM w $ unFlow <$> switch (snd <$> current result) mapDyn fst result chainM :: (MonadSwitch t m) => Chain t m a b -> a -> m (Event t b) chainM c a = switchPromptlyDyn <$> flowM (toFlow c a) loopM :: (MonadSwitch t m) => (a -> m (Event t a)) -> a -> m (Event t a) loopM f a = do rec e <- switchPromptlyDyn <$> holdM (f a) (f <$> e) return e data Chain t m a b where Chain :: (a -> m (Event t b)) -> Chain t m a b (:>>) :: (a -> m (Event t b)) -> Chain t m b c -> Chain t m a c infixr 9 >-> infixr 8 :>> (>->) :: Chain t m a b -> Chain t m b c -> Chain t m a c Chain f >-> c = f :>> c (f :>> c') >-> c = f :>> (c' >-> c) toFlow :: (MonadSwitch t m) => Chain t m a b -> a -> Flow t m (Event t b) toFlow (Chain f) a = Flow $ do e <- f a return (e, end <$ e) where end = Flow $ return (never, never) toFlow (f :>> c) a = Flow $ do e <- f a return (never, toFlow c <$> e)