content: -- |
  -- SPDX-License-Identifier: BSD-3-Clause
  --
  -- Custom extension of 'Semigroup' to 'Monoid' that adds identity +
  -- annihilator elements.
  module Swarm.Util.Erasable where
  
  -- | Extend a semigroup to a monoid by adding an identity ('ENothing') /and/ an
  --   annihilator ('EErase').  That is,
  --
  --   * @ENothing <> e = e <> ENothing = e@
  --   * @EErase <> e = e <> EErase = EErase@
  --
  --   This allows us to "erase" previous values by combining with
  --   'EErase'.  The 'erasableToMaybe' function turns an 'Erasable'
  --   into a 'Maybe' by collapsing 'ENothing' and 'EErase' both back
  --   into 'Nothing'.
  data Erasable e = ENothing | EErase | EJust e
    deriving (Show, Eq, Ord, Functor)
  
  instance Semigroup e => Semigroup (Erasable e) where
    ENothing <> e = e
    e <> ENothing = e
    EErase <> _ = EErase
    _ <> EErase = EErase
    EJust e1 <> EJust e2 = EJust (e1 <> e2)
  
  instance Semigroup e => Monoid (Erasable e) where
    mempty = ENothing
  
  -- | Generic eliminator for 'Erasable' values.
  erasable :: a -> a -> (e -> a) -> Erasable e -> a
  erasable x y z = \case
    ENothing -> x
    EErase -> y
    EJust e -> z e
  
  -- | Convert an 'Erasable' value to 'Maybe', turning both 'ENothing'
  --   and 'EErase' into 'Nothing'.
  erasableToMaybe :: Erasable e -> Maybe e
  erasableToMaybe = erasable Nothing Nothing Just
  
  -- | Inject a 'Maybe' value into 'Erasable' using 'ENothing' and
  -- 'EJust'.
  maybeToErasable :: Maybe e -> Erasable e
  maybeToErasable = maybe ENothing EJust
  