content: {-# LANGUAGE GeneralisedNewtypeDeriving #-}
  {-# LANGUAGE MultiParamTypeClasses #-}
  {-# LANGUAGE TypeFamilies #-}
  module Fresnel.Profunctor.Recall
  ( -- * Recall profunctor
    Recall(..)
  ) where
  
  import Data.Bifunctor
  import Data.Coerce
  import Data.Functor.Const
  import Data.Profunctor
  import Data.Profunctor.Rep as Pro
  import Data.Profunctor.Sieve
  import Data.Profunctor.Unsafe
  
  -- Recall profunctor
  
  -- | @'Recall' e@ is dual to @'Forget' r@: it ignores the argument parameter, substituting in one of its own.
  newtype Recall e a b = Recall { runRecall :: e -> b }
    deriving (Applicative, Functor, Monad, Monoid, Semigroup)
  
  instance Bifunctor (Recall e) where
    bimap _ g = Recall . fmap g . runRecall
    second = fmap
  
  instance Profunctor (Recall e) where
    dimap _ g = Recall . fmap g . runRecall
    lmap = const coerce
    rmap = fmap
    (#.) = const coerce
    (.#) = fmap coerce . const
  
  instance Choice (Recall e) where
    left'  = Recall . fmap Left  . runRecall
    right' = Recall . fmap Right . runRecall
  
  instance Closed (Recall e) where
    closed = Recall . fmap const . runRecall
  
  instance Costrong (Recall e) where
    unfirst  = Recall . fmap fst . runRecall
    unsecond = Recall . fmap snd . runRecall
  
  instance Sieve (Recall e) ((->) e) where
    sieve = const . runRecall
  
  instance Cosieve (Recall e) (Const e) where
    cosieve = lmap getConst . runRecall
  
  instance Pro.Corepresentable (Recall e) where
    type Corep (Recall e) = Const e
  
    cotabulate = Recall . lmap Const
  