{-# LANGUAGE UndecidableInstances #-}
module Reflex.Monad.Supply
( SupplyT
, Supply
, Splitable(..)
, runSupplyT
, evalSupplyT
, runSupply
, evalSupply
, runSplit
, getFresh
, getSplit
) where
import Reflex
import Reflex.Monad.Class
import Control.Monad
import Control.Monad.Identity
import Control.Monad.State.Strict
import Control.Monad.Reader.Class
import Control.Applicative
import Data.Functor
import Data.Traversable
import Data.Map.Strict (Map)
import Prelude
-- | Abstraction for splittable identifier supplies, priovided is an instance for Enum a => [a]
-- but a more efficient supply would be for example, found in the concurrent-supply package.
-- at the cost of determinism.
-- Two parameters, a state 's' and an identifier type 'i'
class Splitable s i | s -> i where
freshId :: s -> (i, s)
splitSupply :: s -> (s, s)
instance Enum a => Splitable [a] [a] where
freshId [] = ([], [toEnum 0])
freshId (x:xs) = (x:xs, succ x:xs)
splitSupply xs = (toEnum 0:xs, xs')
where xs' = snd (freshId xs)
-- | ID Supply transformer for switchable reflex stacks over splittable ID supplies
-- Fresh variables are obtained using getFresh. Admits a MonadSwitch instance.
newtype SupplyT s m a = SupplyT (StateT s m a)
deriving (Functor, Applicative, Monad, MonadTrans, MonadFix)
deriving instance MonadReader st m => MonadReader st (SupplyT s m)
deriving instance MonadWriter w m => MonadWriter w (SupplyT s m)
deriving instance MonadSample t m => MonadSample t (SupplyT s m)
deriving instance MonadHold t m => MonadHold t (SupplyT s m)
-- | Non transformer version
type Supply s a = SupplyT s Identity a
instance MonadState st m => MonadState st (SupplyT s m) where
get = lift get
put = lift . put
-- | Obtain a fresh ID from the Supply
getFresh :: (Monad m, Splitable s i) => SupplyT s m i
getFresh = SupplyT $ state freshId
-- | Obtain a value of the ID Supply which provides distinct ids from those
-- returned from the Monad in future.
getSplit :: (Monad m, Splitable s i) => SupplyT s m s
getSplit = SupplyT $ state splitSupply
-- | Run a non transformer Supply using a split
runSplit :: (Monad m, Splitable s i) => SupplyT s m a -> Supply s (m a)
runSplit m = evalSupplyT m <$> getSplit
-- | Run a SupplyT with a Spittable state
runSupplyT :: Monad m => SupplyT s m a -> s -> m (a, s)
runSupplyT (SupplyT m) = runStateT m
evalSupplyT :: Monad m => SupplyT s m a -> s -> m a
evalSupplyT (SupplyT m) = evalStateT m
runSupply :: Supply s a -> s -> (a, s)
runSupply m = runIdentity . runSupplyT m
evalSupply :: Supply s a -> s -> a
evalSupply m = runIdentity . evalSupplyT m
-- | Helpers for switchMapM implementation
runSupplyMap :: (Ord k, Monad m, Splitable s i) => Map k (SupplyT s m a) -> s -> (Map k (m a), s)
runSupplyMap m = runSupply (traverse runSplit m)
runSupplyMap' :: (Ord k, Monad m, Splitable s i) => Map k (Maybe (SupplyT s m a)) -> s -> (Map k (Maybe (m a)), s)
runSupplyMap' m = runSupply (traverse (traverse runSplit) m)
instance (MonadSwitch t m, Splitable s i) => MonadSwitch t (SupplyT s m) where
switchM (Updated initial e) = do
s <- getSplit
rec
(a, us) <- lift (split <$> switchM (Updated (runSupplyT initial s) $
attachWith (flip runSupplyT) r e))
r <- hold' us
return a
switchMapM (UpdatedMap initial e) = do
(initial', s) <- runSupplyMap initial <$> getSplit
rec
let (um, us) = split $ attachWith (flip runSupplyMap') r e
a <- lift (switchMapM (UpdatedMap initial' um))
r <- hold s us
return a