columbia-0.1.1: src/Data/Columbia/CycleDetection.hs
{-# LANGUAGE ConstraintKinds, ExistentialQuantification, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, UndecidableInstances, ScopedTypeVariables, DeriveDataTypeable, TypeFamilies, StandaloneDeriving, Trustworthy #-}
-- | PolyTraversals that add cycle detection and fixed point construction to any R/W strategy.
module Data.Columbia.CycleDetection where
import Data.Map
import Data.Set (Set)
import Data.Int
import Data.Word
import Data.IORef
import Data.Dynamic hiding (Proxy)
import Data.Generics.SYB.WithClass.Basics
import Data.Columbia.CompoundData
import Data.Columbia.FRecord
import Data.Columbia.Orphans
import Data.Columbia.Integral
import Data.Columbia.DynamicWithCtx
import Generics.Pointless.Functors
import Generics.Pointless.Combinators
import Control.Monad.Reader
import Control.Monad.State
import Control.Monad.Fix
import Prelude hiding (lookup)
import Data.Array
import qualified Data.Array.Unboxed as U
class (Monad m) => StateM m where
type StateOf m :: *
mfy' :: (StateOf m -> (StateOf m, a)) -> m a
instance (Monad m) => StateM(StateT s m) where
type StateOf(StateT s m) = s
mfy' f = get>>= \s -> let(s', x) = f s in (put$!s')>>return x
instance StateM(ReaderT(IORef s) IO) where
type StateOf(ReaderT(IORef s) IO) = s
mfy' f = ask>>=lift.(`atomicModifyIORef'` f)
get' :: (StateM m) => m(StateOf m)
get' = mfy'(\s -> (s, s))
put' s = mfy'(\_ -> (s, ()))
-- | 'isKeyed' may always return false, but if it returns true ever, 'keyCompare'
-- must be well-defined and a valid equivalence relation on values for which
-- 'isKeyed' returns true (i.e. where all values concerned have /isKeyed x=true/).
--The default is to have 'isKeyed' return false on all values.
class KeyComparable t where
isKeyed :: t->Bool
isKeyed _ = False
keyCompare :: t -> t->Ordering
keyCompare _ _ = error"KeyComparable.keyCompare: is not a keyed data type"
data KeyCtx t = (KeyComparable t) => KeyCtx
instance (KeyComparable t) => Sat(KeyCtx t) where dict = KeyCtx
instance Eq(DynamicWithCtx Eq) where
DynamicWithCtx x == DynamicWithCtx x2 = maybe False(==x2) (cast x)
instance KeyComparable(DynamicWithCtx KeyComparable) where
isKeyed(DynamicWithCtx x) = isKeyed x
keyCompare(DynamicWithCtx x) (DynamicWithCtx x2) = maybe(compare(typeOf x) (typeOf x2)) (`keyCompare` x2) (cast x)
instance Ord(DynamicWithCtx KeyComparable) where
-- Here I'm going to assume that all data being compared have keys.
compare = keyCompare
instance Eq(DynamicWithCtx KeyComparable) where
(==) d = (==EQ).keyCompare d
type CycleDetectionR m = StateT(Map Word32 Dynamic) m
type CycleDetectionW m = StateT(Map(DynamicWithCtx KeyComparable) Word32) m
type CycleDetectionRIO = ReaderT(IORef(Map Word32 Dynamic)) IO
type CycleDetectionWIO = ReaderT(IORef(Map(DynamicWithCtx KeyComparable) Word32)) IO
cycleDetect :: forall ctx m t. (MonadFix m, StateM m, StateOf m ~ Map Word32 Dynamic, HasField ctx RWCtx, Data ctx t)
=> PolyTraversal ctx m t
cycleDetect proxy m =
readIntegral>>= \n->
relSeek(-4)>>
lift get'>>=
maybe
(mfix(\x -> do
lift$mfy'(\mp -> (insert n(toDyn x) mp, ()))
m))
(maybe
(fail$"cycleDetect: type mismatch")
return
.fromDynamic)
.lookup n
cycleDetectW :: forall ctx m t. (StateM m, StateOf m ~ Map(DynamicWithCtx KeyComparable) Word32, HasField ctx RWCtx, HasField ctx KeyCtx, Data ctx t)
=> PolyTraversalW ctx m t
cycleDetectW proxy f x =
case hasField(dict :: ctx t) :: KeyCtx t of
KeyCtx ->
let d = dynamicWithCtx x in
lift get'>>= \mp->
maybe
(do
n <- getWriterPosition
seekWriterAtEnd
addr <- getWriterPosition
seekWriter n
when(isKeyed d)$lift$put'$insert d addr mp
f x)
writeIntegral
$lookup d mp
runCycleDetectionR :: (Monad m)
=> ReaderT(SeekableStream(CycleDetectionR m) Word8) (CycleDetectionR m) t
-> ReaderT(SeekableStream m Word8) m t
runCycleDetectionR m = do
s <- ask
lift$evalStateT(runReaderT m(hoistStream lift s)) empty
runCycleDetectionW :: (Monad m)
=> ReaderT(SeekableWriter(CycleDetectionW m) Word8) (CycleDetectionW m) t
-> ReaderT(SeekableWriter m Word8) m t
runCycleDetectionW m = do
sw <- ask
lift$evalStateT(runReaderT m(hoistWriter lift sw)) empty
-----------------------------------
-- | A 'Pair' is a good data structures for associating a key and a value.
data Pair k v = Pair k v deriving (Read, Show, Typeable, Eq, Ord)
instance (Ord k) => KeyComparable(Pair k v) where
isKeyed _ = True
keyCompare(Pair k _) (Pair k2 _) = compare k k2
pairCtor = Constr(AlgConstr 1) "Pair" [] Prefix pairDataType
pairDataType = DataType "Data.Columbia.CycleDetection"(AlgRep[pairCtor])
instance (Sat(ctx(Pair k v)), Data ctx k, Data ctx v) => Data ctx(Pair k v) where
gfoldl _ o f (Pair k v) = f Pair `o` k `o` v
gunfold _ k f _ = k(k(f Pair))
dataTypeOf _ _ = pairDataType
toConstr _ _ = pairCtor
dataCast2 _ f = gcast2 f
instance (Typeable k, Typeable v) => RW(Pair k v)
type instance Rep(Pair k) v = Pair k v
instance ToRep(Pair k) where
rep = id
unrep _ _ = id
val _ = ann
fun _ = ann
-- Some boilerplate instances
instance KeyComparable(t1,t2)
instance KeyComparable(t1,t2,t3)
instance KeyComparable(t1,t2,t3,t4)
instance KeyComparable(t1,t2,t3,t4,t5)
instance KeyComparable(Either t t2)
instance KeyComparable(Maybe t)
instance KeyComparable[t]
instance KeyComparable(Map t u)
instance KeyComparable(Set t)
instance KeyComparable(Array i e)
instance KeyComparable(U.UArray i e)
instance KeyComparable Int
instance KeyComparable Word
instance KeyComparable Int8
instance KeyComparable Word8
instance KeyComparable Int16
instance KeyComparable Word16
instance KeyComparable Int32
instance KeyComparable Word32
instance KeyComparable Int64
instance KeyComparable Word64
instance KeyComparable Float
instance KeyComparable Char
instance KeyComparable Ordering
instance KeyComparable Bool
instance KeyComparable ()
instance KeyComparable ((:+:) f g x)
instance KeyComparable ((:*:) f g x)
instance KeyComparable ((:@:) f g x)
instance KeyComparable (Fix f)
instance KeyComparable (LazyFix f)
instance KeyComparable (Const x x2)
instance KeyComparable (Id x)