diff --git a/crdt.cabal b/crdt.cabal
--- a/crdt.cabal
+++ b/crdt.cabal
@@ -3,7 +3,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           crdt
-version:        0.1
+version:        0.2
 synopsis:       Conflict-free replicated data types
 description:    Definitions of CmRDT and CvRDT. Implementations for some classic CRDTs.
 category:       Distributed Systems
@@ -35,6 +35,7 @@
       CRDT.PNCounter.Cm
       CRDT.PNCounter.Cv
       CRDT.PNCounter.Cv.Internal
+      Data.Semilattice
   default-language: Haskell2010
 
 test-suite test
@@ -45,7 +46,6 @@
   build-depends:
       base >= 4.9 && < 4.10
     , vector
-    , derive
     , QuickCheck
     , tasty
     , tasty-quickcheck
diff --git a/lib/CRDT/Cm.hs b/lib/CRDT/Cm.hs
--- a/lib/CRDT/Cm.hs
+++ b/lib/CRDT/Cm.hs
@@ -13,6 +13,8 @@
 [Commutativity law]
 
     @'update' op1 . 'update' op2 == 'update' op2 . 'update' op1@
+
+Idempotency doesn't need to hold.
 -}
 class CmRDT op where
 
diff --git a/lib/CRDT/Cv.hs b/lib/CRDT/Cv.hs
--- a/lib/CRDT/Cv.hs
+++ b/lib/CRDT/Cv.hs
@@ -1,8 +1,10 @@
+{-# LANGUAGE ConstraintKinds #-}
+
 module CRDT.Cv
     ( CvRDT
     ) where
 
-import Data.Semigroup (Semigroup (..))
+import Data.Semilattice (Semilattice)
 
 {- |
 State-based, or convergent (Cv) replicated data type.
@@ -12,14 +14,6 @@
 Query function is not needed. State itself is exposed.
 In other words, @query = 'id'@.
 
-Laws:
-
-[commutativity]
-
-    @x '<>' y == y '<>' x@
-
-[idempotency]
-
-    @x '<>' x == x@
+Actually, a CvRDT is nothing more a 'Semilattice'.
 -}
-class Semigroup state => CvRDT state
+type CvRDT = Semilattice
diff --git a/lib/CRDT/GCounter/Cv.hs b/lib/CRDT/GCounter/Cv.hs
--- a/lib/CRDT/GCounter/Cv.hs
+++ b/lib/CRDT/GCounter/Cv.hs
@@ -1,8 +1,9 @@
 module CRDT.GCounter.Cv
     ( GCounter
-    , increment
     , initial
     , query
+    -- * Operation
+    , increment
     ) where
 
 import           Data.Monoid         ((<>))
diff --git a/lib/CRDT/GCounter/Cv/Internal.hs b/lib/CRDT/GCounter/Cv/Internal.hs
--- a/lib/CRDT/GCounter/Cv/Internal.hs
+++ b/lib/CRDT/GCounter/Cv/Internal.hs
@@ -8,7 +8,7 @@
 
 -- | Grow-only counter.
 newtype GCounter a = GCounter (Vector a)
-    deriving Eq
+    deriving (Eq, Show)
 
 instance Ord a => Semigroup (GCounter a) where
     GCounter x <> GCounter y = GCounter $ Vector.zipWith max x y
diff --git a/lib/CRDT/LWW.hs b/lib/CRDT/LWW.hs
--- a/lib/CRDT/LWW.hs
+++ b/lib/CRDT/LWW.hs
@@ -19,7 +19,7 @@
     { timestamp :: !Timestamp
     , value     :: !a
     }
-    deriving (Eq, Ord)
+    deriving (Eq, Ord, Show)
 
 instance Ord a => Semigroup (LWW a) where
     (<>) = max
diff --git a/lib/CRDT/PNCounter/Cm.hs b/lib/CRDT/PNCounter/Cm.hs
--- a/lib/CRDT/PNCounter/Cm.hs
+++ b/lib/CRDT/PNCounter/Cm.hs
@@ -10,6 +10,7 @@
 
 -- | Positive-negative counter. Allows incrementing and decrementing.
 data PNCounter a = Increment | Decrement
+    deriving (Bounded, Enum, Show)
 
 instance Num a => CmRDT (PNCounter a) where
     type State (PNCounter a) = a
diff --git a/lib/CRDT/PNCounter/Cv.hs b/lib/CRDT/PNCounter/Cv.hs
--- a/lib/CRDT/PNCounter/Cv.hs
+++ b/lib/CRDT/PNCounter/Cv.hs
@@ -2,10 +2,11 @@
 
 module CRDT.PNCounter.Cv
     ( PNCounter
-    , decrement
-    , increment
     , initial
     , query
+    -- * Operations
+    , decrement
+    , increment
     ) where
 
 import qualified CRDT.GCounter.Cv as GCounter
diff --git a/lib/CRDT/PNCounter/Cv/Internal.hs b/lib/CRDT/PNCounter/Cv/Internal.hs
--- a/lib/CRDT/PNCounter/Cv/Internal.hs
+++ b/lib/CRDT/PNCounter/Cv/Internal.hs
@@ -14,7 +14,7 @@
     { positive :: !(GCounter a)
     , negative :: !(GCounter a)
     }
-    deriving Eq
+    deriving (Eq, Show)
 
 instance Ord a => Semigroup (PNCounter a) where
     PNCounter p1 n1 <> PNCounter p2 n2 = PNCounter (p1 <> p2) (n1 <> n2)
diff --git a/lib/Data/Semilattice.hs b/lib/Data/Semilattice.hs
new file mode 100644
--- /dev/null
+++ b/lib/Data/Semilattice.hs
@@ -0,0 +1,32 @@
+module Data.Semilattice
+    ( Semilattice
+    , (<>)
+    ) where
+
+import           Data.Semigroup (Semigroup)
+import qualified Data.Semigroup as Semigroup
+
+{- |
+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:
+
+[commutativity]
+
+    @x '<>' y == y '<>' x@
+
+[idempotency]
+
+    @x '<>' x == x@
+-}
+class Semigroup a => Semilattice a
+
+-- | Just ('Semigroup.<>'), specialized to 'Semilattice'.
+(<>) :: Semilattice a => a -> a -> a
+(<>) = (Semigroup.<>)
+infixr 6 <>
+{-# INLINE (<>) #-}
diff --git a/test/Instances/Cm.hs b/test/Instances/Cm.hs
--- a/test/Instances/Cm.hs
+++ b/test/Instances/Cm.hs
@@ -1,19 +1,14 @@
 {-# OPTIONS_GHC -Wno-orphans #-}
 
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE StandaloneDeriving #-}
-{-# LANGUAGE TemplateHaskell #-}
-
 module Instances.Cm () where
 
-import Data.DeriveTH   (derives, makeArbitrary)
-import Test.QuickCheck (Arbitrary, arbitrary, choose)
+import Test.QuickCheck (Arbitrary, arbitrary, arbitraryBoundedEnum)
 
 import CRDT.LWW          (LWW (..))
 import CRDT.PNCounter.Cm (PNCounter (..))
 
-derives [makeArbitrary] [''PNCounter, ''LWW]
-
-deriving instance Show (PNCounter a)
+instance Arbitrary (PNCounter a) where
+    arbitrary = arbitraryBoundedEnum
 
-deriving instance Show a => Show (LWW a)
+instance Arbitrary a => Arbitrary (LWW a) where
+    arbitrary = Write <$> arbitrary <*> arbitrary
diff --git a/test/Instances/Cv.hs b/test/Instances/Cv.hs
--- a/test/Instances/Cv.hs
+++ b/test/Instances/Cv.hs
@@ -17,9 +17,5 @@
 
 deriving instance Arbitrary a => Arbitrary (GCounter a)
 
-deriving instance Show a => Show (GCounter a)
-
 instance Arbitrary a => Arbitrary (PNCounter a) where
     arbitrary = PNCounter <$> arbitrary <*> arbitrary
-
-deriving instance Show a => Show (PNCounter a)
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -2,7 +2,7 @@
 {-# LANGUAGE ScopedTypeVariables #-}
 
 import Data.Proxy            (Proxy (..))
-import Data.Semigroup        ((<>))
+import Data.Semilattice      (Semilattice, (<>))
 import Test.QuickCheck       (Arbitrary, Small (..))
 import Test.Tasty            (TestTree, defaultMain, testGroup)
 import Test.Tasty.QuickCheck (testProperty)
@@ -42,18 +42,18 @@
                 PncCv.query (PncCv.decrement i c) == pred (PncCv.query c)
         ]
     , testGroup "Cm"
-        [ cmrdtCommutativity (Proxy :: Proxy (PncCm.PNCounter Int)) ]
+        [ cmrdtLaws (Proxy :: Proxy (PncCm.PNCounter Int)) ]
     ]
 
 lww :: TestTree
 lww = testGroup "LWW"
-    [ testGroup "Cm" [ cmrdtCommutativity (Proxy :: Proxy (LWW Int)) ]
+    [ testGroup "Cm" [ cmrdtLaws (Proxy :: Proxy (LWW Int)) ]
     , testGroup "Cv" [ cvrdtLaws (Proxy :: Proxy (LWW Int)) ]
     ]
 
-cvrdtLaws
+semilatticeLaws
     :: forall a . (Arbitrary a, CvRDT a, Eq a, Show a) => Proxy a -> TestTree
-cvrdtLaws _ = testGroup "CvRDT laws"
+semilatticeLaws _ = testGroup "Semilattice laws"
     [ testProperty "associativity" associativity
     , testProperty "commutativity" commutativity
     , testProperty "idempotency"   idempotency
@@ -68,13 +68,16 @@
     idempotency :: a -> Bool
     idempotency x = x <> x == x
 
-cmrdtCommutativity
+cvrdtLaws :: (Arbitrary a, Semilattice a, Eq a, Show a) => Proxy a -> TestTree
+cvrdtLaws = semilatticeLaws
+
+cmrdtLaws
     :: forall op
     . ( Arbitrary op, CmRDT op, Show op
       , Arbitrary (State op), Eq (State op), Show (State op)
       )
     => Proxy op -> TestTree
-cmrdtCommutativity _ = testProperty "CmRDT law: commutativity" commutativity
+cmrdtLaws _ = testProperty "CmRDT law: commutativity" commutativity
   where
     commutativity :: op -> op -> State op -> Bool
     commutativity op1 op2 x =
