module Data.Profunctor.Arrow.Adapter (
    Coyoneda
  , AdapterA
  , liftAdapter
  , foldAdapter
  , runAdapterT
  , runAdapterM
  , runAdapterW
) where

import Control.Arrow (Kleisli(..))
import Control.Category hiding ((.), id)
import Control.Comonad (Comonad, Cokleisli(..))
import Data.Profunctor
import Data.Profunctor.Arrow.Free
import Data.Profunctor.Yoneda

import Prelude

type AdapterA p = Free (Coyoneda p)

{-# INLINE liftAdapter #-}
-- | TODO: Document
--
liftAdapter :: p a b -> AdapterA p a b
liftAdapter p = Free (Coyoneda id id p) (Parr id)

{-# INLINE foldAdapter #-}
-- | TODO: Document
--
foldAdapter :: Category q => Profunctor q => p :-> q -> AdapterA p a b -> q a b
foldAdapter pq = foldFree (runAdapterT pq)

{-# INLINE runAdapterT #-}
-- | TODO: Document
--
runAdapterT :: Profunctor q => p :-> q -> Coyoneda p a b -> q a b
runAdapterT pq (Coyoneda l r p) = dimap l r (pq p)

{-# INLINE runAdapterM #-}
-- | TODO: Document
--
runAdapterM :: Monad m => (forall x y. p x y -> x -> m y) -> AdapterA p a b -> a -> m b
runAdapterM f = runKleisli . foldAdapter (Kleisli . f)

{-# INLINE runAdapterW #-}
-- | TODO: Document
--
runAdapterW :: Comonad w => (forall x y. p x y -> w x -> y) -> AdapterA p a b -> w a -> b
runAdapterW f = runCokleisli . foldAdapter (Cokleisli . f)