{-# LANGUAGE RankNTypes #-}
module Unbound.Generics.LocallyNameless.Internal.Iso where

import Data.Profunctor (Profunctor(..))
import Data.Functor.Identity (Identity(..))

data Exchange a b s t = Exchange (s -> a) (b -> t)

instance Profunctor (Exchange a b) where
  dimap f g (Exchange h k) = Exchange (h . f ) (g . k)

type Iso s t a b = forall p f . (Profunctor p, Functor f) => p a (f b) -> p s (f t)

type AnIso s t a b = Exchange a b a (Identity b) -> Exchange a b s (Identity t)

iso :: (s -> a) -> (b -> t) -> Iso s t a b
iso sa bt = dimap sa (fmap bt)
{-# INLINE iso #-}

from :: AnIso s t a b -> Iso b a t s
from l = withIso l $  \ sa bt -> iso bt sa
{-# INLINE from #-}

withIso :: AnIso s t a b -> ((s -> a) -> (b -> t) -> r) -> r
withIso ai k =
  case ai (Exchange id Identity) of
   Exchange sa bt -> k sa (runIdentity . bt)
{-# INLINE withIso #-}