crdt-2.1: lib/CRDT/Cv/PNCounter.hs
{-# LANGUAGE NamedFieldPuns #-}
module CRDT.Cv.PNCounter
( PNCounter (..)
, initial
, query
-- * Operations
, decrement
, increment
) where
import Data.Semigroup (Semigroup (..))
import Data.Semilattice (Semilattice)
import CRDT.Cv.GCounter (GCounter)
import qualified CRDT.Cv.GCounter as GCounter
{- |
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
{ positive :: !(GCounter a)
, negative :: !(GCounter a)
}
deriving (Eq, Show)
instance Ord a => Semigroup (PNCounter a) where
PNCounter p1 n1 <> PNCounter p2 n2 = PNCounter (p1 <> p2) (n1 <> n2)
-- | See 'CvRDT'
instance Ord a => Semilattice (PNCounter a)
-- | Get value from the state
query :: Num a => PNCounter a -> a
query PNCounter{positive, negative} =
GCounter.query positive - GCounter.query negative
-- | Decrement counter
decrement
:: Num a
=> Word -- ^ replica id
-> PNCounter a
-> PNCounter a
decrement i pnc@PNCounter{negative} =
pnc{negative = GCounter.increment i negative}
-- | Increment counter
increment
:: Num a
=> Word -- ^ replica id
-> PNCounter a
-> PNCounter a
increment i pnc@PNCounter{positive} =
pnc{positive = GCounter.increment i positive}
-- | Initial state
initial :: PNCounter a
initial = PNCounter{positive = GCounter.initial, negative = GCounter.initial}