content: -- | An abstract key/value store.
  module Build.Store (
      -- * Hashing
      Hash, Hashable (..),
  
      -- * Store
      Store, getValue, putValue, getHash, getInfo, putInfo, mapInfo,
      initialise
      ) where
  
  -- | A 'Hash' is used for efficient tracking and sharing of build results. We
  -- use @newtype Hash a = Hash a@ for prototyping.
  newtype Hash a = Hash a deriving (Eq, Ord,Show)
  
  instance Functor Hash where
      fmap f (Hash a) = Hash (f a)
  
  instance Applicative Hash where
      pure = Hash
      Hash f <*> Hash a = Hash (f a)
  
  class Ord a => Hashable a where
      -- | Compute the hash of a given value. We typically assume cryptographic
      -- hashing, e.g. SHA256.
      hash :: a -> Hash a
  
  instance Hashable Int where
      hash = Hash
  
  instance Hashable Integer where
      hash = Hash
  
  instance Hashable a => Hashable [a] where
      hash = Hash
  
  instance Hashable a => Hashable (Hash a) where
      hash = Hash
  
  instance (Hashable a, Hashable b) => Hashable (a, b) where
      hash = Hash
  
  -- | An abstract datatype for a key/value store with build information of type @i@.
  data Store i k v = Store { info :: i, values :: k -> v }
  
  -- | Read the build information.
  getInfo :: Store i k v -> i
  getInfo = info
  
  -- | Read the value of a key.
  getValue :: k -> Store i k v -> v
  getValue = flip values
  
  -- | Read the hash of a key's value. In some cases may be implemented more
  -- efficiently than @hash . getValue k@.
  getHash :: Hashable v => k -> Store i k v -> Hash v
  getHash k = hash . getValue k
  
  -- | Write the build information.
  putInfo :: i -> Store i k v -> Store i k v
  putInfo i s = s { info = i }
  
  -- | Modify the build information.
  mapInfo :: (i -> j) -> Store i k v -> Store j k v
  mapInfo f (Store i kv) = Store (f i) kv
  
  -- | Update the value of a key.
  putValue :: Eq k => k -> v -> Store i k v -> Store i k v
  putValue k v s = s { values = \key -> if key == k then v else values s key }
  
  -- | Initialise the store.
  initialise :: i -> (k -> v) -> Store i k v
  initialise = Store
  