crdt-1.0: lib/CRDT/Cm.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE TypeFamilies #-}
module CRDT.Cm
( CmRDT (..)
, concurrent
) where
import Algebra.PartialOrd (PartialOrd (leq))
import Data.Observe (Observe (..))
import LamportClock (Clock)
-- | TODO(cblp, 2017-09-29) import from lattices >= 1.6
comparable :: PartialOrd a => a -> a -> Bool
comparable a b = a `leq` b || b `leq` a
-- | Not comparable, i. e. ¬(a ≤ b) ∧ ¬(b ≤ a).
concurrent :: PartialOrd a => a -> a -> Bool
concurrent a b = not $ comparable a b
{- |
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__.
[Payload]
Internal state of a replica.
[Operation]
User's request to update.
[Update]
Operation to be applied to other replicas.
For many types /operation/ and /update/ may be the same.
But for 'CRDT.Cm.LWW.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 (Observe payload, Eq (Observed payload), PartialOrd update)
=> CmRDT payload op update
| payload -> op, op -> update, update -> payload
where
-- | Precondition for 'updateAtSource'.
-- Calculates if the operation is applicable to the current state.
updateAtSourcePre :: op -> payload -> Bool
updateAtSourcePre _ _ = True
-- | Generate an update to the local and remote replicas.
-- Doesn't have sense if 'updateAtSourcePre' is false.
--
-- May or may not use clock.
updateAtSource :: Clock m => op -> m update
-- | Apply an update to the payload.
-- An invalid update must be ignored.
updateDownstream :: update -> payload -> payload