-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Conflict-free replicated data types -- -- Definitions of CmRDT and CvRDT. Implementations for some classic -- CRDTs. @package crdt @version 1.0 module LamportClock -- | Unique process identifier newtype Pid Pid :: Word32 -> Pid type Time = Word -- | Timestamps are assumed unique, totally ordered, and consistent with -- causal order; i.e., if assignment 1 happened-before assignment 2, the -- former’s timestamp is less than the latter’s. data Timestamp Timestamp :: !Time -> !Pid -> Timestamp class Applicative f => Clock f -- | Get another unique timestamp newTimestamp :: Clock f => f Timestamp -- | TODO(cblp, 2017-09-28) Use bounded-intmap type LamportClock = State (EnumMap Pid Time) runLamportClock :: LamportClock a -> a type Process = ReaderT Pid LamportClock runProcess :: Pid -> Process a -> LamportClock a -- | XXX Make sure all subsequent calls to newTimestamp return -- timestamps greater than all prior calls. barrier :: [Pid] -> LamportClock () instance GHC.Show.Show LamportClock.Timestamp instance GHC.Classes.Ord LamportClock.Timestamp instance GHC.Classes.Eq LamportClock.Timestamp instance GHC.Show.Show LamportClock.Pid instance GHC.Classes.Ord LamportClock.Pid instance GHC.Classes.Eq LamportClock.Pid instance GHC.Enum.Enum LamportClock.Pid instance LamportClock.Clock LamportClock.Process module Data.Semilattice -- | A semilattice. -- -- It may be a join-semilattice, or meet-semilattice, it doesn't matter. -- -- If it matters for you, use package lattices. -- -- In addition to Semigroup, Semilattice defines this laws: -- --
class Semigroup a => Semilattice a -- | Just (<>), specialized to Semilattice. merge :: Semilattice a => a -> a -> a infixr 6 `merge` module Data.Observe class Observe a where type Observed a :: Type where { type family Observed a :: Type; } observe :: Observe a => a -> Observed a module CRDT.Cv.Max newtype Max a :: * -> * Max :: a -> Max a [getMax] :: Max a -> a -- | Construct new value point :: a -> Max a query :: Max a -> a instance GHC.Classes.Ord a => Data.Semilattice.Semilattice (Data.Semigroup.Max a) module CRDT.Cv.GSet -- | Grow-only set type GSet = Set -- | update add :: Ord a => a -> GSet a -> GSet a -- | initialization initial :: GSet a query :: Ord a => a -> GSet a -> Bool instance GHC.Classes.Ord a => Data.Semilattice.Semilattice (Data.Set.Base.Set a) module CRDT.Cv -- | State-based, or convergent (Cv) replicated data type. -- -- Update is any function modifying state. -- -- Query function is not needed. State itself is exposed. In other words, -- query = id. Some types may offer more convenient query -- functions. -- -- Actually, a CvRDT is nothing more a Semilattice. type CvRDT = Semilattice module CRDT.Cv.GCounter -- | Grow-only counter. newtype GCounter a GCounter :: (IntMap a) -> GCounter a -- | Initial state initial :: GCounter a -- | Get value from the state query :: Num a => GCounter a -> a -- | Increment counter increment :: Num a => Word -> GCounter a -> GCounter a instance GHC.Show.Show a => GHC.Show.Show (CRDT.Cv.GCounter.GCounter a) instance GHC.Classes.Eq a => GHC.Classes.Eq (CRDT.Cv.GCounter.GCounter a) instance GHC.Classes.Ord a => Data.Semigroup.Semigroup (CRDT.Cv.GCounter.GCounter a) instance GHC.Classes.Ord a => Data.Semilattice.Semilattice (CRDT.Cv.GCounter.GCounter a) module CRDT.Cv.LWW -- | Last write wins. Assuming timestamp is unique. data LWW a LWW :: !Timestamp -> !a -> LWW a [timestamp] :: LWW a -> !Timestamp [value] :: LWW a -> !a -- | Initialize state initial :: Clock f => a -> f (LWW a) -- | Change state assign :: Clock f => a -> LWW a -> f (LWW a) -- | Query state query :: LWW a -> a instance GHC.Show.Show a => GHC.Show.Show (CRDT.Cv.LWW.LWW a) instance GHC.Classes.Eq (CRDT.Cv.LWW.LWW a) instance GHC.Classes.Ord (CRDT.Cv.LWW.LWW a) instance Data.Semigroup.Semigroup (CRDT.Cv.LWW.LWW a) instance Data.Semilattice.Semilattice (CRDT.Cv.LWW.LWW a) module CRDT.Cv.PNCounter -- | Positive-negative counter. Allows incrementing and decrementing. Nice -- example of combining of existing CvRDT (GCounter in this case) -- to create another CvRDT. data PNCounter a PNCounter :: !(GCounter a) -> !(GCounter a) -> PNCounter a [positive] :: PNCounter a -> !(GCounter a) [negative] :: PNCounter a -> !(GCounter a) -- | Initial state initial :: PNCounter a -- | Get value from the state query :: Num a => PNCounter a -> a -- | Decrement counter decrement :: Num a => Word -> PNCounter a -> PNCounter a -- | Increment counter increment :: Num a => Word -> PNCounter a -> PNCounter a instance GHC.Show.Show a => GHC.Show.Show (CRDT.Cv.PNCounter.PNCounter a) instance GHC.Classes.Eq a => GHC.Classes.Eq (CRDT.Cv.PNCounter.PNCounter a) instance GHC.Classes.Ord a => Data.Semigroup.Semigroup (CRDT.Cv.PNCounter.PNCounter a) instance GHC.Classes.Ord a => Data.Semilattice.Semilattice (CRDT.Cv.PNCounter.PNCounter a) module CRDT.Cm -- | Operation-based, or commutative (Cm) replicated data type. -- ---- ∀ up1 up2 s . -- concurrent up1 up2 ==> -- observe (updateDownstream up1 . updateDownstream up2 $ s) == -- observe (updateDownstream up2 . updateDownstream up1 $ s) ---- -- Idempotency doesn't need to hold. class (Observe payload, Eq (Observed payload), PartialOrd update) => CmRDT payload op update | payload -> op, op -> update, update -> payload where updateAtSourcePre _ _ = True -- | Precondition for updateAtSource. Calculates if the operation is -- applicable to the current state. updateAtSourcePre :: CmRDT payload op update => op -> payload -> Bool -- | Generate an update to the local and remote replicas. Doesn't have -- sense if updateAtSourcePre is false. -- -- May or may not use clock. updateAtSource :: (CmRDT payload op update, Clock m) => op -> m update -- | Apply an update to the payload. An invalid update must be ignored. updateDownstream :: CmRDT payload op update => update -> payload -> payload -- | Not comparable, i. e. ¬(a ≤ b) ∧ ¬(b ≤ a). concurrent :: PartialOrd a => a -> a -> Bool module CRDT.Cm.Counter newtype Counter a Counter :: a -> Counter a data CounterOp a Increment :: CounterOp a Decrement :: CounterOp a initial :: Num a => Counter a instance GHC.Show.Show (CRDT.Cm.Counter.CounterOp a) instance GHC.Classes.Eq (CRDT.Cm.Counter.CounterOp a) instance GHC.Enum.Enum (CRDT.Cm.Counter.CounterOp a) instance GHC.Enum.Bounded (CRDT.Cm.Counter.CounterOp a) instance GHC.Show.Show a => GHC.Show.Show (CRDT.Cm.Counter.Counter a) instance (GHC.Num.Num a, GHC.Classes.Eq a) => CRDT.Cm.CmRDT (CRDT.Cm.Counter.Counter a) (CRDT.Cm.Counter.CounterOp a) (CRDT.Cm.Counter.CounterOp a) instance Data.Observe.Observe (CRDT.Cm.Counter.Counter a) instance Algebra.PartialOrd.PartialOrd (CRDT.Cm.Counter.CounterOp a) module CRDT.Cm.GSet -- | Grow-only set type GSet = Set newtype Add a Add :: a -> Add a initial :: GSet a -- | query lookup lookup :: Ord a => a -> GSet a -> Bool instance GHC.Show.Show a => GHC.Show.Show (CRDT.Cm.GSet.Add a) instance GHC.Classes.Eq a => GHC.Classes.Eq (CRDT.Cm.GSet.Add a) instance GHC.Classes.Ord a => CRDT.Cm.CmRDT (Data.Set.Base.Set a) (CRDT.Cm.GSet.Add a) (CRDT.Cm.GSet.Add a) instance Data.Observe.Observe (CRDT.Cv.GSet.GSet a) instance GHC.Classes.Eq a => Algebra.PartialOrd.PartialOrd (CRDT.Cm.GSet.Add a) module CRDT.Cm.LWW -- | Last write wins. Assuming timestamp is unique. data LWW a LWW :: !Timestamp -> !a -> LWW a [timestamp] :: LWW a -> !Timestamp [value] :: LWW a -> !a newtype Assign a Assign :: a -> Assign a instance GHC.Show.Show a => GHC.Show.Show (CRDT.Cm.LWW.Assign a) instance GHC.Classes.Eq a => GHC.Classes.Eq (CRDT.Cm.LWW.Assign a) instance Algebra.PartialOrd.PartialOrd (CRDT.Cv.LWW.LWW a) instance GHC.Classes.Eq a => CRDT.Cm.CmRDT (CRDT.Cv.LWW.LWW a) (CRDT.Cm.LWW.Assign a) (CRDT.Cv.LWW.LWW a) instance Data.Observe.Observe (CRDT.Cv.LWW.LWW a) -- | TODO(cblp, 2017-09-29) USet? module CRDT.Cm.TPSet newtype TPSet a TPSet :: Set a -> TPSet a [payload] :: TPSet a -> Set a data TPSetOp a Add :: a -> TPSetOp a Remove :: a -> TPSetOp a initial :: TPSet a -- | query lookup lookup :: Ord a => a -> TPSet a -> Bool -- | Generate an update to the local and remote replicas. Doesn't have -- sense if updateAtSourcePre is false. -- -- May or may not use clock. updateAtSource :: (CmRDT payload op update, Clock m) => op -> m update -- | Apply an update to the payload. An invalid update must be ignored. updateDownstream :: CmRDT payload op update => update -> payload -> payload instance GHC.Show.Show a => GHC.Show.Show (CRDT.Cm.TPSet.TPSetOp a) instance GHC.Classes.Eq a => GHC.Classes.Eq (CRDT.Cm.TPSet.TPSetOp a) instance GHC.Show.Show a => GHC.Show.Show (CRDT.Cm.TPSet.TPSet a) instance GHC.Classes.Ord a => CRDT.Cm.CmRDT (CRDT.Cm.TPSet.TPSet a) (CRDT.Cm.TPSet.TPSetOp a) (CRDT.Cm.TPSet.TPSetOp a) instance Data.Observe.Observe (CRDT.Cm.TPSet.TPSet a) instance GHC.Classes.Eq a => Algebra.PartialOrd.PartialOrd (CRDT.Cm.TPSet.TPSetOp a)