{-# LANGUAGE UndecidableInstances #-}
module Reflex.Monad.Class
( MonadSwitch (..)
, MonadReflex
, module Reflex
, module Reflex.Switching
, module Reflex.Updated
, module Control.Monad.Writer.Class
) where
import Reflex
import Reflex.Updated
import Reflex.Switching
import Data.Maybe
import Data.Functor
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Control.Monad
import Control.Monad.Reader
import Control.Monad.Writer
import Control.Monad.Writer.Class
import Prelude
-- 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
--
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.
switchMapM :: Ord k => UpdatedMap t k (m a) -> m (UpdatedMap t k a)
instance MonadSwitch t m => MonadSwitch t (ReaderT e m) where
switchM u = do
env <- ask
lift $ switchM (flip runReaderT env <$> u)
switchMapM um = do
env <- ask
lift . switchMapM $ flip runReaderT env <$> um
instance (MonadSwitch t m, SwitchMerge t w) => MonadSwitch t (WriterT w m) where
switchM u = do
(a, w) <- lift $ split <$> switchM (runWriterT <$> u)
tell =<< switching' w
return a
switchMapM um = do
(a, w) <- lift $ split <$> switchMapM (runWriterT <$> um)
tell =<< switchMerge' w
return a
-- | A few conversions for switchM in terms of switchMapM
maybeToMap :: Maybe a -> Map () a
maybeToMap Nothing = mempty
maybeToMap (Just a) = Map.singleton () a
mapToMaybe :: Map () a -> Maybe a
mapToMaybe m = listToMaybe $ Map.elems m
toMap :: Reflex t => Updated t (Maybe a) -> UpdatedMap t () a
toMap (Updated initial e) = UpdatedMap (maybeToMap initial) (Map.singleton () <$> e)
fromMap :: Reflex t => UpdatedMap t () a -> Updated t (Maybe a)
fromMap (UpdatedMap initial e) = Updated (mapToMaybe initial) (fmapMaybe mapToMaybe e)
{-# ANN module "HLint: ignore Use import/export shortcut" #-}