split-morphism-0.1.0.0: src/Control/Lens/SplitMono.hs
{-# LANGUAGE Rank2Types #-}
{-# LANGUAGE TemplateHaskell #-}
module Control.Lens.SplitMono where
import Control.Lens
import Data.Functor.Invariant.TH
{- | A split monomorphism, which we can think of as a weaker `Iso` a b where `a` is a "smaller" type.
So `reverseGet . get` remains an identity but `get . reverseGet` is merely idempotent (i.e., it normalizes values in `b`).
The following statements hold:
- `reverseGet` is a "retraction" of `get`,
- `get` is a "section" of `reverseGet`,
- `a` is a "retract" of `b`,
- the pair `(reverseGet, get)` is a "splitting" of the idempotent `get . reverseGet`.
-}
data SplitMono a b = SplitMono
{ get :: a -> b
, reverseGet :: b -> a
}
$(deriveInvariant ''SplitMono)
-- | `reverseGet . get`, yielding a normalized formatted value. Subsequent get/reverseGet cycles are idempotent.
normalize :: SplitMono a b -> b -> b
normalize (SplitMono f g) = f . g
-- | Compose with another SplitMono.
composeSplitMono :: SplitMono a b -> SplitMono b c -> SplitMono a c
composeSplitMono (SplitMono x y) (SplitMono q w) =
SplitMono (q . x) (y . w)
-- | Compose with an Iso.
composeIso :: SplitMono a b -> Iso' b c -> SplitMono a c
composeIso (SplitMono x y) i =
SplitMono ((^. i) . x) (y . review i)
-- | An Isomorphism is trivially a SplitMono.
fromIso :: Iso' a b -> SplitMono a b
fromIso i = SplitMono (^. i) (review i)