{-# LANGUAGE TypeFamilies, FlexibleContexts, UndecidableInstances #-}
module Data.Profunctor.Corepresentable 
  ( CorepresentableProfunctor(..) 
  ) where

import Data.Functor
import Data.Functor.Identity
import Data.Profunctor
import Control.Arrow
import Data.Profunctor.Composition
import Data.Functor.Compose

class Functor (Corep k) => CorepresentableProfunctor k where
  type Corep k :: * -> *
  cotabulatePro :: (d -> Corep k c) -> k d c
  coindexPro :: k d c -> d -> Corep k c 
  
instance CorepresentableProfunctor (->) where
  type Corep (->) = Identity
  cotabulatePro f = runIdentity . f
  coindexPro f = Identity . f 

instance Functor m => CorepresentableProfunctor (Kleisli m) where
  type Corep (Kleisli m) = m
  cotabulatePro = Kleisli
  coindexPro = runKleisli 

instance Functor f => CorepresentableProfunctor (UpStar f) where
  type Corep (UpStar f) = f
  cotabulatePro = UpStar
  coindexPro = runUpStar

instance (CorepresentableProfunctor c, CorepresentableProfunctor d) => CorepresentableProfunctor (Procompose c d) where
  type Corep (Procompose c d) = Compose (Corep c) (Corep d)
  cotabulatePro f = Procompose (cotabulatePro (getCompose . f)) (cotabulatePro id)
  coindexPro (Procompose f g) d = Compose $ coindexPro g <$> coindexPro f d