-- 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 2.0 module LamportClock -- | Unique process identifier newtype Pid Pid :: Int -> 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 type LamportClock = State LamportTime 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 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 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.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 -- | Partial order for causal semantics. Values of some type may be ordered -- and causally-ordered different ways. class CausalOrd a before :: CausalOrd a => a -> a -> Bool -- | Operation-based, or commutative (Cm) replicated data type. -- --

Implementation

-- -- In Haskell, a CmRDT implementation consists of 3 types — a -- payload, an operation (op) and an -- update. -- -- -- -- For many types operation and update may be the same. But -- for LWW, for instance, this rule doesn't hold: user can request -- only value, and type attaches a timestamp to it. -- --

Additional constraint — commutativity law

-- -- Concurrent updates are observed equally. -- --
--   ∀ 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 (CausalOrd u, Eq (View u)) => CmRDT u where type Op u type Payload u type View u type Op u = u type View u = Payload u updateAtSourcePre _ _ = True updateAtSource = pure view = id where { type family Op u; type family Payload u; type family View u; type Op u = u; type View u = Payload u; } -- | Precondition for updateAtSource. Calculates if the operation is -- applicable to the current state. updateAtSourcePre :: CmRDT u => Op u -> Payload u -> 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 u, Clock m) => Op u -> m u -- | 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 u, Clock m, Op u ~ u) => Op u -> m u -- | Apply an update to the payload. An invalid update must be ignored. updateDownstream :: CmRDT u => u -> Payload u -> Payload u -- | Extract user-visible value from payload view :: CmRDT u => Payload u -> View u -- | Extract user-visible value from payload view :: (CmRDT u, Payload u ~ View u) => Payload u -> View u -- | Not comparable, i. e. ¬(a ≤ b) ∧ ¬(b ≤ a). concurrent :: CausalOrd a => a -> a -> Bool module CRDT.Cm.Counter data Counter a Increment :: Counter a Decrement :: Counter a instance GHC.Show.Show (CRDT.Cm.Counter.Counter a) instance GHC.Classes.Eq (CRDT.Cm.Counter.Counter a) instance GHC.Enum.Enum (CRDT.Cm.Counter.Counter a) instance GHC.Enum.Bounded (CRDT.Cm.Counter.Counter a) instance (GHC.Num.Num a, GHC.Classes.Eq a) => CRDT.Cm.CmRDT (CRDT.Cm.Counter.Counter a) instance CRDT.Cm.CausalOrd (CRDT.Cm.Counter.Counter a) module CRDT.Cm.GSet newtype GSet a Add :: a -> GSet a instance GHC.Show.Show a => GHC.Show.Show (CRDT.Cm.GSet.GSet a) instance GHC.Classes.Eq a => GHC.Classes.Eq (CRDT.Cm.GSet.GSet a) instance GHC.Classes.Ord a => CRDT.Cm.CmRDT (CRDT.Cm.GSet.GSet a) instance GHC.Classes.Eq a => CRDT.Cm.CausalOrd (CRDT.Cm.GSet.GSet a) -- | TODO(cblp, 2017-09-29) USet? module CRDT.Cm.TPSet data TPSet a Add :: a -> TPSet a Remove :: a -> TPSet a -- | 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 u, Clock m) => Op u -> m u -- | Apply an update to the payload. An invalid update must be ignored. updateDownstream :: CmRDT u => u -> Payload u -> Payload u instance GHC.Show.Show a => GHC.Show.Show (CRDT.Cm.TPSet.TPSet a) instance GHC.Classes.Eq a => GHC.Classes.Eq (CRDT.Cm.TPSet.TPSet a) instance GHC.Classes.Ord a => CRDT.Cm.CmRDT (CRDT.Cm.TPSet.TPSet a) instance GHC.Classes.Eq a => CRDT.Cm.CausalOrd (CRDT.Cm.TPSet.TPSet a) module CRDT.LWW -- | Last write wins. Assuming timestamp is unique. This type is both -- CmRDT and CvRDT. data LWW a LWW :: !a -> !Timestamp -> LWW a [value] :: LWW a -> !a [timestamp] :: LWW a -> !Timestamp -- | Initialize state initial :: Clock f => a -> f (LWW a) -- | Change state as CvRDT operation. Current value is ignored, because new -- timestamp is always greater. assign :: Clock f => a -> LWW a -> f (LWW a) -- | Query state query :: LWW a -> a -- | Change state as CmRDT operation newtype Assign a Assign :: a -> Assign a instance GHC.Show.Show a => GHC.Show.Show (CRDT.LWW.Assign a) instance GHC.Classes.Eq a => GHC.Classes.Eq (CRDT.LWW.Assign a) instance GHC.Show.Show a => GHC.Show.Show (CRDT.LWW.LWW a) instance GHC.Classes.Eq (CRDT.LWW.LWW a) instance GHC.Classes.Ord (CRDT.LWW.LWW a) instance Data.Semigroup.Semigroup (CRDT.LWW.LWW a) instance Data.Semilattice.Semilattice (CRDT.LWW.LWW a) instance CRDT.Cm.CausalOrd (CRDT.LWW.LWW a) instance GHC.Classes.Eq a => CRDT.Cm.CmRDT (CRDT.LWW.LWW a)