packages feed

swarm-0.4: src/Swarm/Util/Erasable.hs

-- |
-- 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