module Data.MultiChange (
-- * Type
MultiChange,
-- * Construction
singleton,
fromList,
-- * Monad structure
map,
return,
join,
bind,
-- * Multi composition
compose,
composeMap
) where
-- Prelude
import Prelude hiding (id, (.), map, return)
import qualified Prelude
{-FIXME:
After establishment of the Applicative–Monad proposal, we have to optionally
hide join.
-}
-- Control
import Control.Category
import Control.Arrow (second)
import Control.Monad (liftM)
-- Data
import Data.Monoid
import Data.Foldable as Foldable
import qualified Data.List as List
import Data.DList (DList)
import qualified Data.DList as DList
import Data.Incremental
-- * Type
newtype MultiChange p = MultiChange (Dual (DList p)) deriving Monoid
instance Show p => Show (MultiChange p) where
showsPrec prec xs = showParen (prec > 10) $
showString "fromList " . shows (toList xs)
-- NOTE: This is basically taken from Data.Sequence.
instance Read p => Read (MultiChange p) where
readsPrec prec = readParen (prec > 10) $ \ str -> do
("fromList", rest) <- lex str
(list, rest') <- reads rest
Prelude.return (fromList list, rest')
-- NOTE: This is basically taken from Data.Sequence.
instance Foldable MultiChange where
foldMap fun (MultiChange (Dual dList)) = foldMap fun dList
foldr next init (MultiChange (Dual dList)) = Foldable.foldr next init dList
{-FIXME:
Starting with GHC 7.10, Foldable.foldr can probably be written just
foldr, because the “Burning Bridges Proposal” has been implemented
(meaning that Prelude functions like foldr are now the more general
versions from Data.Foldable and Data.Traversable).
-}
instance Change p => Change (MultiChange p) where
type Value (MultiChange p) = Value p
change $$ val = List.foldl' (flip ($$)) val (toList change)
{-FIXME:
Starting with GHC 7.10, List.foldl' can probably be written just
foldl', because the “Burning Bridges Proposal” has been implemented
(meaning that Data.List functions like foldl' are now the more general
versions from Data.Foldable and Data.Traversable).
-}
-- * Construction
singleton :: p -> MultiChange p
singleton = MultiChange . Dual . DList.singleton
{-NOTE:
The lists are “in diagramatic order” (first atomic change at the beginning).
-}
fromList :: [p] -> MultiChange p
fromList = MultiChange . Dual . DList.fromList
-- * Monad structure
map :: Trans p q -> Trans (MultiChange p) (MultiChange q)
map trans = stTrans (\ val -> do
~(val', prop) <- toSTProc trans val
let multiProp change = do
atomics' <- mapM prop (toList change)
Prelude.return (fromList atomics')
Prelude.return (val', multiProp))
return :: Trans p (MultiChange p)
return = simpleTrans id singleton
join :: Trans (MultiChange (MultiChange p)) (MultiChange p)
join = compose
bind :: Trans p (MultiChange q) -> Trans (MultiChange p) (MultiChange q)
bind = composeMap
-- * Multi composition
compose :: Monoid p => Trans (MultiChange p) p
compose = simpleTrans id (mconcat . reverse . toList)
{-FIXME:
Check whether the use of mconcat . reverse is questionable regarding space
usage or strictness. If it is, consider using foldr (flip mappend) mempty
instead.
-}
composeMap :: Monoid q => Trans p q -> Trans (MultiChange p) q
composeMap trans = compose . map trans