{-# 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}