{-# LANGUAGE Rank2Types      #-}
{-# LANGUAGE TemplateHaskell #-}

module Control.Lens.Wedge where

import Control.Lens
import Data.Functor.Invariant.TH

{- | Composition of a `Control.Lens.SplitMono.SplitMono` and a `Control.Lens.SplitEpi.SplitEpi`, yielding an even weaker structure where neither
`reverseGet . get` and `get . reverseGet` is an identity but both are idempotent.
-}
data Wedge a b = Wedge
    { get :: a -> b
    , reverseGet :: b -> a
    }
$(deriveInvariant ''Wedge)

-- | Normalize `a` via a round-trip through `b`.
normalizeA :: Wedge a b -> a -> a
normalizeA w = reverseGet w . get w

-- | Normalize `b` via a round-trip through `a`.
normalizeB :: Wedge a b -> b -> b
normalizeB w = get w . reverseGet w

-- | Swapping `get` and `reverseGet` yields a Wedge.
reverse :: Wedge a b -> Wedge b a
reverse (Wedge f g) = Wedge g f

-- | Compose with another Wedge.
composeWedge :: Wedge a b -> Wedge b c -> Wedge a c
composeWedge (Wedge x y) (Wedge q w) =
    Wedge (q . x) (y . w)

-- | Compose with an Iso.
composeIso :: Wedge a b -> Iso' b c -> Wedge a c
composeIso (Wedge x y) i =
    Wedge ((^. i) . x) (y . review i)

-- | An Isomorphism is trivially a Wedge.
fromIso :: Iso' a b -> Wedge a b
fromIso i = Wedge (^. i) (review i)