{-# LANGUAGE CPP #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE Rank2Types #-}
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 707
{-# LANGUAGE DeriveDataTypeable #-}
#endif
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ <= 707
{-# LANGUAGE KindSignatures #-}
#endif
#if __GLASGOW_HASKELL__ >= 702 && __GLASGOW_HASKELL__ < 710
{-# LANGUAGE Trustworthy #-}
#endif
-----------------------------------------------------------------------------
-- |
-- Copyright   :  (C) 2013-2016 Edward Kmett, Gershom Bazerman and Derek Elkins
-- License     :  BSD-style (see the file LICENSE)
--
-- Maintainer  :  Edward Kmett <[email protected]>
-- Stability   :  provisional
-- Portability :  portable
--
-- The Day convolution of two contravariant functors is a contravariant
-- functor.
--
-- <http://ncatlab.org/nlab/show/Day+convolution>
----------------------------------------------------------------------------

module Data.Functor.Contravariant.Day
  ( Day(..)
  , day
  , runDay
  , assoc, disassoc
  , swapped
  , intro1, intro2
  , day1, day2
  , diag
  , trans1, trans2
  ) where

#if __GLASGOW_HASKELL__ < 710
import Control.Applicative
#endif
import Data.Functor.Contravariant
import Data.Functor.Contravariant.Rep
import Data.Proxy
import Data.Tuple (swap)
#ifdef __GLASGOW_HASKELL__
import Data.Typeable
#endif

-- | The Day convolution of two contravariant functors.
data Day f g a = forall b c. Day (f b) (g c) (a -> (b, c))
#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ >= 707
  deriving Typeable
#endif

-- | Construct the Day convolution
--
-- @
-- 'day1' ('day' f g) = f
-- 'day2' ('day' f g) = g
-- @
day :: f a -> g b -> Day f g (a, b)
day :: forall (f :: * -> *) a (g :: * -> *) b.
f a -> g b -> Day f g (a, b)
day f a
fa g b
gb = forall (f :: * -> *) (g :: * -> *) a b c.
f b -> g c -> (a -> (b, c)) -> Day f g a
Day f a
fa g b
gb forall a. a -> a
id

#if defined(__GLASGOW_HASKELL__) && __GLASGOW_HASKELL__ < 707
instance (Typeable1 f, Typeable1 g) => Typeable1 (Day f g) where
    typeOf1 tfga = mkTyConApp dayTyCon [typeOf1 (fa tfga), typeOf1 (ga tfga)]
        where fa :: t f (g :: * -> *) a -> f a
              fa = undefined
              ga :: t (f :: * -> *) g a -> g a
              ga = undefined

dayTyCon :: TyCon
#if MIN_VERSION_base(4,4,0)
dayTyCon = mkTyCon3 "contravariant" "Data.Functor.Contravariant.Day" "Day"
#else
dayTyCon = mkTyCon "Data.Functor.Contravariant.Day.Day"
#endif

#endif

instance Contravariant (Day f g) where
  contramap :: forall a' a. (a' -> a) -> Day f g a -> Day f g a'
contramap a' -> a
f (Day f b
fb g c
gc a -> (b, c)
abc) = forall (f :: * -> *) (g :: * -> *) a b c.
f b -> g c -> (a -> (b, c)) -> Day f g a
Day f b
fb g c
gc (a -> (b, c)
abc forall b c a. (b -> c) -> (a -> b) -> a -> c
. a' -> a
f)

instance (Representable f, Representable g) => Representable (Day f g) where
  type Rep (Day f g) = (Rep f, Rep g)

  tabulate :: forall a. (a -> Rep (Day f g)) -> Day f g a
tabulate a -> Rep (Day f g)
a2fg = forall (f :: * -> *) (g :: * -> *) a b c.
f b -> g c -> (a -> (b, c)) -> Day f g a
Day (forall (f :: * -> *) a. Representable f => (a -> Rep f) -> f a
tabulate forall a b. (a, b) -> a
fst) (forall (f :: * -> *) a. Representable f => (a -> Rep f) -> f a
tabulate forall a b. (a, b) -> b
snd) forall a b. (a -> b) -> a -> b
$ \a
a -> let b :: Rep (Day f g)
b = a -> Rep (Day f g)
a2fg a
a in (Rep (Day f g)
b,Rep (Day f g)
b)

  index :: forall a. Day f g a -> a -> Rep (Day f g)
index (Day f b
fb g c
gc a -> (b, c)
abc) a
a = case a -> (b, c)
abc a
a of
    (b
b, c
c) -> (forall (f :: * -> *) a. Representable f => f a -> a -> Rep f
index f b
fb b
b, forall (f :: * -> *) a. Representable f => f a -> a -> Rep f
index g c
gc c
c)
  {-# INLINE index #-}

  contramapWithRep :: forall b a.
(b -> Either a (Rep (Day f g))) -> Day f g a -> Day f g b
contramapWithRep b -> Either a (Rep (Day f g))
d2eafg (Day f b
fb g c
gc a -> (b, c)
abc) = forall (f :: * -> *) (g :: * -> *) a b c.
f b -> g c -> (a -> (b, c)) -> Day f g a
Day (forall (f :: * -> *) b a.
Representable f =>
(b -> Either a (Rep f)) -> f a -> f b
contramapWithRep forall a. a -> a
id f b
fb) (forall (f :: * -> *) b a.
Representable f =>
(b -> Either a (Rep f)) -> f a -> f b
contramapWithRep forall a. a -> a
id g c
gc) forall a b. (a -> b) -> a -> b
$ \b
d -> case b -> Either a (Rep (Day f g))
d2eafg b
d of
    Left a
a -> case a -> (b, c)
abc a
a of
      (b
b, c
c) -> (forall a b. a -> Either a b
Left b
b, forall a b. a -> Either a b
Left c
c)
    Right (Rep f
vf, Rep g
vg) -> (forall a b. b -> Either a b
Right Rep f
vf, forall a b. b -> Either a b
Right Rep g
vg)
  {-# INLINE tabulate #-}


-- | Break apart the Day convolution of two contravariant functors.
runDay :: (Contravariant f, Contravariant g) => Day f g a -> (f a, g a)
runDay :: forall (f :: * -> *) (g :: * -> *) a.
(Contravariant f, Contravariant g) =>
Day f g a -> (f a, g a)
runDay (Day f b
fb g c
gc a -> (b, c)
abc) =
  ( forall (f :: * -> *) a' a.
Contravariant f =>
(a' -> a) -> f a -> f a'
contramap (forall a b. (a, b) -> a
fst forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> (b, c)
abc) f b
fb
  , forall (f :: * -> *) a' a.
Contravariant f =>
(a' -> a) -> f a -> f a'
contramap (forall a b. (a, b) -> b
snd forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> (b, c)
abc) g c
gc
  )

-- | Day convolution provides a monoidal product. The associativity
-- of this monoid is witnessed by 'assoc' and 'disassoc'.
--
-- @
-- 'assoc' . 'disassoc' = 'id'
-- 'disassoc' . 'assoc' = 'id'
-- 'contramap' f '.' 'assoc' = 'assoc' '.' 'contramap' f
-- @
assoc :: Day f (Day g h) a -> Day (Day f g) h a
assoc :: forall (f :: * -> *) (g :: * -> *) (h :: * -> *) a.
Day f (Day g h) a -> Day (Day f g) h a
assoc (Day f b
fb (Day g b
gd h c
he c -> (b, c)
cde) a -> (b, c)
abc) = forall (f :: * -> *) (g :: * -> *) a b c.
f b -> g c -> (a -> (b, c)) -> Day f g a
Day (forall (f :: * -> *) (g :: * -> *) a b c.
f b -> g c -> (a -> (b, c)) -> Day f g a
Day f b
fb g b
gd forall a. a -> a
id) h c
he forall a b. (a -> b) -> a -> b
$ \a
a ->
  case c -> (b, c)
cde forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> a -> (b, c)
abc a
a of
    (b
b, (b
d, c
e)) -> ((b
b, b
d), c
e)

-- | Day convolution provides a monoidal product. The associativity
-- of this monoid is witnessed by 'assoc' and 'disassoc'.
--
-- @
-- 'assoc' . 'disassoc' = 'id'
-- 'disassoc' . 'assoc' = 'id'
-- 'contramap' f '.' 'disassoc' = 'disassoc' '.' 'contramap' f
-- @
disassoc :: Day (Day f g) h a -> Day f (Day g h) a
disassoc :: forall (f :: * -> *) (g :: * -> *) (h :: * -> *) a.
Day (Day f g) h a -> Day f (Day g h) a
disassoc (Day (Day f b
fd g c
ge b -> (b, c)
bde) h c
hc a -> (b, c)
abc) = forall (f :: * -> *) (g :: * -> *) a b c.
f b -> g c -> (a -> (b, c)) -> Day f g a
Day f b
fd (forall (f :: * -> *) (g :: * -> *) a b c.
f b -> g c -> (a -> (b, c)) -> Day f g a
Day g c
ge h c
hc forall a. a -> a
id) forall a b. (a -> b) -> a -> b
$ \a
a ->
  case a -> (b, c)
abc a
a of
    (b
b, c
c) -> case b -> (b, c)
bde b
b of
      (b
d, c
e) -> (b
d, (c
e, c
c))

-- | The monoid for Day convolution /in Haskell/ is symmetric.
--
-- @
-- 'contramap' f '.' 'swapped' = 'swapped' '.' 'contramap' f
-- @
swapped :: Day f g a -> Day g f a
swapped :: forall (f :: * -> *) (g :: * -> *) a. Day f g a -> Day g f a
swapped (Day f b
fb g c
gc a -> (b, c)
abc) = forall (f :: * -> *) (g :: * -> *) a b c.
f b -> g c -> (a -> (b, c)) -> Day f g a
Day g c
gc f b
fb (forall a b. (a, b) -> (b, a)
swap forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> (b, c)
abc)

-- | Proxy serves as the unit of Day convolution.
--
-- @
-- 'day1' '.' 'intro1' = 'id'
-- 'contramap' f '.' 'intro1' = 'intro1' '.' 'contramap' f
-- @
intro1 :: f a -> Day Proxy f a
intro1 :: forall (f :: * -> *) a. f a -> Day Proxy f a
intro1 f a
fa = forall (f :: * -> *) (g :: * -> *) a b c.
f b -> g c -> (a -> (b, c)) -> Day f g a
Day forall {k} (t :: k). Proxy t
Proxy f a
fa forall a b. (a -> b) -> a -> b
$ \a
a -> ((),a
a)

-- | Proxy serves as the unit of Day convolution.
--
-- @
-- 'day2' '.' 'intro2' = 'id'
-- 'contramap' f '.' 'intro2' = 'intro2' '.' 'contramap' f
-- @
intro2 :: f a -> Day f Proxy a
intro2 :: forall (f :: * -> *) a. f a -> Day f Proxy a
intro2 f a
fa = forall (f :: * -> *) (g :: * -> *) a b c.
f b -> g c -> (a -> (b, c)) -> Day f g a
Day f a
fa forall {k} (t :: k). Proxy t
Proxy forall a b. (a -> b) -> a -> b
$ \a
a -> (a
a,())

-- | In Haskell we can do general purpose elimination, but in a more general setting
-- it is only possible to eliminate the unit.
--
-- @
-- 'day1' '.' 'intro1' = 'id'
-- 'day1' = 'fst' '.' 'runDay'
-- 'contramap' f '.' 'day1' = 'day1' '.' 'contramap' f
-- @
day1 :: Contravariant f => Day f g a -> f a
day1 :: forall (f :: * -> *) (g :: * -> *) a.
Contravariant f =>
Day f g a -> f a
day1 (Day f b
fb g c
_ a -> (b, c)
abc) = forall (f :: * -> *) a' a.
Contravariant f =>
(a' -> a) -> f a -> f a'
contramap (forall a b. (a, b) -> a
fst forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> (b, c)
abc) f b
fb

-- | In Haskell we can do general purpose elimination, but in a more general setting
-- it is only possible to eliminate the unit.
-- @
-- 'day2' '.' 'intro2' = 'id'
-- 'day2' = 'snd' '.' 'runDay'
-- 'contramap' f '.' 'day2' = 'day2' '.' 'contramap' f
-- @
day2 :: Contravariant g => Day f g a -> g a
day2 :: forall (g :: * -> *) (f :: * -> *) a.
Contravariant g =>
Day f g a -> g a
day2 (Day f b
_ g c
gc a -> (b, c)
abc) = forall (f :: * -> *) a' a.
Contravariant f =>
(a' -> a) -> f a -> f a'
contramap (forall a b. (a, b) -> b
snd forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> (b, c)
abc) g c
gc

-- | Diagonalize the Day convolution:
--
-- @
-- 'day1' '.' 'diag' = 'id'
-- 'day2' '.' 'diag' = 'id'
-- 'runDay' '.' 'diag' = \a -> (a,a)
-- 'contramap' f . 'diag' = 'diag' . 'contramap' f
-- @

diag :: f a -> Day f f a
diag :: forall (f :: * -> *) a. f a -> Day f f a
diag f a
fa = forall (f :: * -> *) (g :: * -> *) a b c.
f b -> g c -> (a -> (b, c)) -> Day f g a
Day f a
fa f a
fa forall a b. (a -> b) -> a -> b
$ \a
a -> (a
a,a
a)

-- | Apply a natural transformation to the left-hand side of a Day convolution.
--
-- This respects the naturality of the natural transformation you supplied:
--
-- @
-- 'contramap' f '.' 'trans1' fg = 'trans1' fg '.' 'contramap' f
-- @
trans1 :: (forall x. f x -> g x) -> Day f h a -> Day g h a
trans1 :: forall (f :: * -> *) (g :: * -> *) (h :: * -> *) a.
(forall x. f x -> g x) -> Day f h a -> Day g h a
trans1 forall x. f x -> g x
fg (Day f b
fb h c
hc a -> (b, c)
abc) = forall (f :: * -> *) (g :: * -> *) a b c.
f b -> g c -> (a -> (b, c)) -> Day f g a
Day (forall x. f x -> g x
fg f b
fb) h c
hc a -> (b, c)
abc

-- | Apply a natural transformation to the right-hand side of a Day convolution.
--
-- This respects the naturality of the natural transformation you supplied:
--
-- @
-- 'contramap' f '.' 'trans2' fg = 'trans2' fg '.' 'contramap' f
-- @
trans2 :: (forall x. g x -> h x) -> Day f g a -> Day f h a
trans2 :: forall (g :: * -> *) (h :: * -> *) (f :: * -> *) a.
(forall x. g x -> h x) -> Day f g a -> Day f h a
trans2 forall x. g x -> h x
gh (Day f b
fb g c
gc a -> (b, c)
abc) = forall (f :: * -> *) (g :: * -> *) a b c.
f b -> g c -> (a -> (b, c)) -> Day f g a
Day f b
fb (forall x. g x -> h x
gh g c
gc) a -> (b, c)
abc