reflex-transformers 0.1 → 0.2
raw patch · 7 files changed
+103/−47 lines, 7 files
Files
- reflex-transformers.cabal +1/−1
- src/Reflex/Monad.hs +60/−31
- src/Reflex/Monad/Class.hs +10/−10
- src/Reflex/Monad/ReaderWriter.hs +9/−2
- src/Reflex/Monad/ReflexM.hs +2/−0
- src/Reflex/Switching.hs +12/−1
- src/Reflex/Updated.hs +9/−2
reflex-transformers.cabal view
@@ -1,5 +1,5 @@ name: reflex-transformers-version: 0.1+version: 0.2 license: BSD3 license-file: LICENSE cabal-version: >= 1.10
src/Reflex/Monad.hs view
@@ -9,21 +9,21 @@ module Reflex.Monad ( module Reflex.Monad.Class - , holdM+ , widgetHold - , collectMapM- , collectM+ , mapView + , collection , collect - , Flow (..)- , flowM+ , Workflow (..)+ , workflow , Chain (..)- , chainM+ , chain , (>->) - , loopM+ , loop ) where @@ -46,9 +46,11 @@ --holdM :: (MonadSwitch t m) => m a -> Event t (m a) -> m (Dynamic t a)-holdM initial e = holdDyn' =<< switchM (Updated initial e)+-- | Hold a monadic widget and update it whenever the Event provides a new+-- monadic widget, swapping out the previously active widget.+-- Returns a Dynamic giving the return values of widgets created+widgetHold :: (MonadSwitch t m) => m a -> Event t (m a) -> m (Dynamic t a)+widgetHold initial e = holdDyn' =<< switchM (Updated initial e) withIds :: (MonadReflex t m) => [a] -> Event t [a] -> m (Map Int a, Event t (Map Int a))@@ -59,13 +61,18 @@ zipFrom n = Map.fromList . zip [n..] -+-- | Non monadic version of collection, builds a collection from an initial list and a list of updated values+-- items remove themselves upon the event triggering.+-- returns an UpdatedMap with keys assigned to items in ascending order 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)- +collect initial added = runReflexM $ collection (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 ++-- | Builds a collection of widgets from an initial list and events providing new widgets to create+-- as with collect, items remove themselves upon the event triggering. +-- returns an UpdatedMap with keys assigned to items in ascending order+collection :: (MonadSwitch t m) => [m (a, Event t ())] -> Event t [m (a, Event t ())] -> m (UpdatedMap t Int a)+collection initial added = do (initialMap, addedMap) <- withIds initial added rec @@ -81,9 +88,11 @@ +-- | Provides a view into a Dynamic Map value, where sub-views are created using a function passed in+-- returns a Dynamic Map of values returned from child views upon creation. -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+mapView :: (MonadSwitch t m, Ord k) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Dynamic t (Map k a))+mapView input childView = do inputViews <- mapDyn (Map.mapWithKey itemView) input let updates = shallowDiff (current inputViews) (updated inputViews) @@ -94,30 +103,47 @@ 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+-- | Recursive Workflow datatype, see 'workflow' below++newtype Workflow t m a = Workflow { unFlow :: m (a, Event t (Workflow t m a)) }+++-- | Provide a widget which swaps itself out for another widget upon an event+-- (recursively)+-- Useful if the sequence of widgets needs to return a value (as opposed to passing it +-- down the chain).++workflow :: (MonadSwitch t m) => Workflow t m a -> m (Dynamic t a)+workflow (Workflow w) = do rec - result <- holdM w $ unFlow <$> switch (snd <$> current result)+ result <- widgetHold 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)+-- | Provide a way of chaining widgets of type (a -> m (Event t b))+-- where one widgets swaps out the old widget.+-- De-couples the return type as compared to using 'workflow' +chain :: (MonadSwitch t m) => Chain t m a b -> a -> m (Event t b)+chain c a = switchPromptlyDyn <$> workflow (toFlow c a) -loopM :: (MonadSwitch t m) => (a -> m (Event t a)) -> a -> m (Event t a)-loopM f a = do++-- | Provide a way of looping (a -> m (Event t a)), each iteration switches+-- out the previous iteration.+-- Can be used with +loop :: (MonadSwitch t m) => (a -> m (Event t a)) -> a -> m (Event t a)+loop f a = do rec- e <- switchPromptlyDyn <$> holdM (f a) (f <$> e)+ e <- switchPromptlyDyn <$> widgetHold (f a) (f <$> e) return e - +-- | Data type wrapping chainable widgets of the type (a -> m (Event t a)) 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@@ -127,17 +153,20 @@ infixr 8 :>> +-- | Compose two 'Chain' values passing the output event of one +-- into the construction function of the next.+ (>->) :: 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 +toFlow :: (MonadSwitch t m) => Chain t m a b -> a -> Workflow t m (Event t b)+toFlow (Chain f) a = Workflow $ do e <- f a return (e, end <$ e)- where end = Flow $ return (never, never)+ where end = Workflow $ return (never, never) -toFlow (f :>> c) a = Flow $ do+toFlow (f :>> c) a = Workflow $ do e <- f a return (never, toFlow c <$> e)
src/Reflex/Monad/Class.hs view
@@ -36,26 +36,26 @@ import Prelude --- Constraint type to capture common usage together+-- | Constraint type to capture common usage together type MonadReflex t m = (Reflex t, MonadHold t m, MonadFix m) class (MonadReflex t m) => MonadSwitch t m | m -> t where - -- | Map the result of an initial monadic action and updates and swap - -- it out with a new one whenever the event provided fires.- -- returns an 'Updated' giving the initial value plus updates- -- + -- | Map the result of an initial monadic action and updates and swap + -- it out with a new one whenever the event provided fires.+ -- returns an Updated giving the initial value plus updates+ switchM :: Updated t (m a) -> m (Updated t a) switchM u = do m <- switchMapM (toMap (Just <$> u)) return $ fromJust <$> fromMap m - -- | Similar to holdM but operating on a collection of widgets- -- provided as an 'UpdatedMap'.- -- switchM/switchM' can be implemented in terms of switchMapM - -- therefore switchMapM is a minimal implementation.+ -- | Similar to holdM but operating on a collection of widgets+ -- provided as an 'UpdatedMap'.+ -- switchM/switchM' can be implemented in terms of switchMapM + -- therefore switchMapM is a minimal implementation. switchMapM :: Ord k => UpdatedMap t k (m a) -> m (UpdatedMap t k a) @@ -88,7 +88,7 @@ - -- | A few conversions for switchM in terms of switchMapM+-- A few conversions for switchM in terms of switchMapM maybeToMap :: Maybe a -> Map () a maybeToMap Nothing = mempty maybeToMap (Just a) = Map.singleton () a
src/Reflex/Monad/ReaderWriter.hs view
@@ -28,7 +28,14 @@ import Prelude - ++-- | Fusion between ReaderT and WriterT (But not StateT unlike RWST) +-- which is switchable using MonadSwitch+--+-- Uses implementation based on RSST which implements WriterT using state+-- in order to avoid space leaks incurred by the original WriterT+-- + newtype ReaderWriterT r w m a = ReaderWriterT (RSST r w () m a) deriving (Functor, Applicative, Monad, MonadTrans, MonadFix, MonadReader r, MonadWriter w) @@ -55,7 +62,7 @@ return (a, w) -execReaderWriterT :: (Monad m, Monoid w) => ReaderWriterT r w m a -> r -> m w+execReaderWriterT :: (Functor m, Monad m, Monoid w) => ReaderWriterT r w m a -> r -> m w execReaderWriterT m r = snd <$> runReaderWriterT m r
src/Reflex/Monad/ReflexM.hs view
@@ -10,6 +10,8 @@ import Prelude +-- | Base Monad which sits at the bottom of a (pure) switching Monad transformer stack providing+-- the base switching capabilities. newtype ReflexM t a = ReflexM { runReflexM :: forall m. MonadReflex t m => m a }
src/Reflex/Switching.hs view
@@ -1,6 +1,12 @@ {-# LANGUAGE UndecidableInstances #-} -module Reflex.Switching where+module Reflex.Switching + ( Switching (..)+ , SwitchMerge (..)+ , switching'+ , switchMerge'+ + ) where import Reflex.Class hiding (constant)@@ -24,6 +30,7 @@ switching :: MonadHold t m => r -> Event t r -> m r class (Switching t r, Monoid r) => SwitchMerge t r where + -- | Switching for a changing collections of reactive types switchMerge :: (MonadFix m, MonadHold t m, Ord k) => Map k r -> Event t (Map k (Maybe r)) -> m r @@ -82,9 +89,13 @@ instance (Reflex t) => Switching t () where switching _ _ = pure () + +-- | Helper which takes an UpdatedMap as one argument (instead of initial value, update event separately) switchMerge' :: (Reflex t, SwitchMerge t r, MonadFix m, MonadHold t m, Ord k) => UpdatedMap t k r -> m r switchMerge' (UpdatedMap initial e) = switchMerge initial e ++-- | Helper which takes an Updated as one argument (instead of initial value, update event separately) switching' :: (Reflex t, Switching t r, MonadFix m, MonadHold t m) => Updated t r -> m r switching' (Updated initial e) = switching initial e
src/Reflex/Updated.hs view
@@ -25,9 +25,10 @@ import Prelude -+-- | Data type for a collection (a map) which is updated by providing differences data UpdatedMap t k a = UpdatedMap (Map k a) (Event t (Map k (Maybe a))) +-- | Data type for an initial value which is updated, mainly useful for it's Functor instance data Updated t a = Updated a (Event t a) @@ -42,11 +43,12 @@ instance Reflex t => FunctorWithIndex k (UpdatedMap t k) where imap f (UpdatedMap initial changes) = UpdatedMap (imap f initial) (imap (fmap . f) <$> changes) -+-- | Generalized unzip - probably not the best place for this! split :: Functor f => f (a, b) -> (f a, f b) split f = (fst <$> f, snd <$> f) +-- | Turn an UpdatedMap into a Dynamic by applying the differences to the initial value holdMapDyn :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Dynamic t (Map k a)) holdMapDyn (UpdatedMap initial changes) = foldDyn (flip (ifoldr modify)) initial changes @@ -55,21 +57,26 @@ modify k (Just item) items = Map.insert k item items +-- | Hold an UpdatedMap as a behavior by applying differences to the initial value holdMap :: (Reflex t, MonadHold t m, MonadFix m, Ord k) => UpdatedMap t k a -> m (Behavior t (Map k a)) holdMap = (current <$>) . holdMapDyn +-- | Hold an 'Updated' as a Dynamic by replacing the initial value with updates holdDyn' :: (Reflex t, MonadHold t m, MonadFix m) => Updated t a -> m (Dynamic t a) holdDyn' (Updated initial changes) = holdDyn initial changes +-- | Hold an 'Updated' as a Behavior by replacing the initial value with updates hold' :: (Reflex t, MonadHold t m, MonadFix m) => Updated t a -> m (Behavior t a) hold' (Updated initial changes) = hold initial changes +-- | Find the shallow (structural) differences between two Maps shallowDiff' :: (Ord k) => Map k a -> Map k b -> Map k (Maybe b) shallowDiff' m m' = (Just <$> m' Map.\\ m) <> (const Nothing <$> m Map.\\ m') +-- | Track the shallow (structural) differences between a Behavior and an Event shallowDiff :: (Reflex t, Ord k) => Behavior t (Map k a) -> Event t (Map k b) -> Event t (Map k (Maybe b)) shallowDiff currentItems updatedItems = ffilter (not . Map.null) $ attachWith shallowDiff' currentItems updatedItems