{-# LANGUAGE GADTs                 #-}
{-# LANGUAGE PolyKinds             #-}
{-# LANGUAGE QuantifiedConstraints #-}
{-# LANGUAGE RankNTypes            #-}
{- | @'Sub' a b@ witnesses a zero-cost conversion @a -> b@.

=== Example

Think about the following code:

> -- | A pair @(x::a, y::a)@, but guaranteed @x <= y@
> newtype Range a = MkRange (a,a)
>
> getRange :: Range a -> (a,a)
> getRange = coerce
> mkRange :: Ord a => a -> a -> Range a
> mkRange x y = if x <= y then MkRange (x,y) else MkRange (y,x)

If you want to provide this type from a library you maintain,
you would want to keep @Range@ abstract from outside of the module.

A user may want to convert @[Range a]@ to @[(a,a)]@ without actually
traversing the list. This is possible if the user have access to the
internals, or you export a @Coercion (Range a) (a,a)@ value. But doing so
breaks the guarantee, because it also allows to use @Coercible@ in the other
direction, as in @coerce (10,5) :: Range Int@.

By exporting only @Sub (Range a) (a,a)@ value from your module,
this user can get @Sub [Range a] [(a,a)]@ using 'mapR',
without being able to make an invalid value.

-}
module Data.Type.Coercion.Sub(
  Sub(),
  sub, toSub, upcastWith, equiv, gequiv,

  coercionIsSub,

  instantiate,
  mapR, contramapR,
  bimapR, prodR, prod3R, sumR, dimapR, arrR
) where

import           Data.Coerce
import           Data.Type.Coercion

import           Data.Bifunctor                  (Bifunctor)
import           Data.Functor.Contravariant      (Contravariant)
import           Data.Profunctor                 (Profunctor)

import           Data.Type.Coercion.Sub.Internal

-- | Make a directed witness of @'coerce' :: a -> b@.
sub :: Coercible a b => Sub a b
sub :: forall {k} (a :: k) (b :: k). Coercible a b => Sub a b
sub = forall k (a :: k) (b :: k). Coercion a b -> Sub a b
Sub forall {k} (a :: k) (b :: k). Coercible a b => Coercion a b
Coercion

-- | Make a directed witness of @'coerce' :: a -> b@, from a 'Coercion' value.
toSub :: Coercion a b -> Sub a b
toSub :: forall k (a :: k) (b :: k). Coercion a b -> Sub a b
toSub = forall k (a :: k) (b :: k). Coercion a b -> Sub a b
Sub

-- | Type-safe cast
upcastWith :: Sub a b -> a -> b
upcastWith :: forall a b. Sub a b -> a -> b
upcastWith (Sub Coercion a b
Coercion) = coerce :: forall a b. Coercible a b => a -> b
coerce

-- | All 'Coercion' can be seen as 'Sub'
coercionIsSub :: Sub (Coercion a b) (Sub a b)
coercionIsSub :: forall {k} (a :: k) (b :: k). Sub (Coercion a b) (Sub a b)
coercionIsSub = forall k (a :: k) (b :: k). Coercion a b -> Sub a b
Sub forall {k} (a :: k) (b :: k). Coercible a b => Coercion a b
Coercion

-- | `Sub` relation in both direction means there is `Coercion` relation.
equiv :: Sub a b -> Sub b a -> Coercion a b
equiv :: forall {k} (a :: k) (b :: k). Sub a b -> Sub b a -> Coercion a b
equiv Sub a b
ab Sub b a
ba = forall {k} (a :: k) (b :: k) r.
Sub a b -> Sub b a -> (Coercible a b => r) -> r
gequiv Sub a b
ab Sub b a
ba forall {k} (a :: k) (b :: k). Coercible a b => Coercion a b
Coercion

-- | Generalized 'equiv'
gequiv :: Sub a b -> Sub b a -> (Coercible a b => r) -> r
gequiv :: forall {k} (a :: k) (b :: k) r.
Sub a b -> Sub b a -> (Coercible a b => r) -> r
gequiv (Sub Coercion a b
Coercion) (Sub Coercion b a
Coercion) Coercible a b => r
k = Coercible a b => r
k

{-

Note: evaluating both arguments of `equiv` is necessary.
One might notice the following typechecks.

    equiv :: Sub a b -> Sub b a -> Coercion a b
    equiv (Sub Coercion) _ = Coercion

But this implementation allows inverting `Sub a b` circumventing the restriction;

    bad :: Sub a b -> Sub b a
    bad ab =
      let ba = upcastWith coercionIsSub (equiv ab ba)
      in ba

This is prevented by evaluating both arguments of `equiv`, making `bad ab` a bottom.

-}

-----------------------------

-- | For a @Sub@ relation between type constructors @f@ and @g@,
--   create an instance of subtype relation @Sub (f a) (g a)@ for any type
--   parameter @a@.
instantiate :: forall j k (f :: j -> k) (g :: j -> k) (a :: j).
  Sub f g -> Sub (f a) (g a)
instantiate :: forall j k (f :: j -> k) (g :: j -> k) (a :: j).
Sub f g -> Sub (f a) (g a)
instantiate (Sub Coercion f g
Coercion) = forall {k} (a :: k) (b :: k). Coercible a b => Sub a b
sub

-- | Extend subtype relation covariantly.
mapR :: ( forall x x'. Coercible x x' => Coercible (t x) (t x')
        , Functor t)
     => Sub a b -> Sub (t a) (t b)
mapR :: forall (t :: * -> *) a b.
(forall x x'. Coercible x x' => Coercible (t x) (t x'),
 Functor t) =>
Sub a b -> Sub (t a) (t b)
mapR (Sub Coercion a b
Coercion) = forall k (a :: k) (b :: k). Coercion a b -> Sub a b
Sub forall {k} (a :: k) (b :: k). Coercible a b => Coercion a b
Coercion

-- | Extend subtype relation contravariantly.
contramapR :: ( forall x x'. Coercible x x' => Coercible (t x) (t x')
              , Contravariant t)
           => Sub a b -> Sub (t b) (t a)
contramapR :: forall (t :: * -> *) a b.
(forall x x'. Coercible x x' => Coercible (t x) (t x'),
 Contravariant t) =>
Sub a b -> Sub (t b) (t a)
contramapR (Sub Coercion a b
Coercion) = forall k (a :: k) (b :: k). Coercion a b -> Sub a b
Sub forall {k} (a :: k) (b :: k). Coercible a b => Coercion a b
Coercion

-- | Extend subtype relation over a 'Bifunctor'.
bimapR :: ( forall x x' y y'.
              (Coercible x x', Coercible y y') => Coercible (t x y) (t x' y')
          , Bifunctor t)
       => Sub a a' -> Sub b b' -> Sub (t a b) (t a' b')
bimapR :: forall (t :: * -> * -> *) a a' b b'.
(forall x x' y y'.
 (Coercible x x', Coercible y y') =>
 Coercible (t x y) (t x' y'),
 Bifunctor t) =>
Sub a a' -> Sub b b' -> Sub (t a b) (t a' b')
bimapR (Sub Coercion a a'
Coercion) (Sub Coercion b b'
Coercion) = forall k (a :: k) (b :: k). Coercion a b -> Sub a b
Sub forall {k} (a :: k) (b :: k). Coercible a b => Coercion a b
Coercion

-- | 'bimapR' specialized for the pair type '(a,b)'
prodR :: Sub a a' -> Sub b b' -> Sub (a,b) (a',b')
prodR :: forall a a' b b'. Sub a a' -> Sub b b' -> Sub (a, b) (a', b')
prodR = forall (t :: * -> * -> *) a a' b b'.
(forall x x' y y'.
 (Coercible x x', Coercible y y') =>
 Coercible (t x y) (t x' y'),
 Bifunctor t) =>
Sub a a' -> Sub b b' -> Sub (t a b) (t a' b')
bimapR

infixr 3 `prodR`

-- | 'bimapR' specialized for 'Either'
sumR :: Sub a a' -> Sub b b' -> Sub (Either a b) (Either a' b')
sumR :: forall a a' b b'.
Sub a a' -> Sub b b' -> Sub (Either a b) (Either a' b')
sumR = forall (t :: * -> * -> *) a a' b b'.
(forall x x' y y'.
 (Coercible x x', Coercible y y') =>
 Coercible (t x y) (t x' y'),
 Bifunctor t) =>
Sub a a' -> Sub b b' -> Sub (t a b) (t a' b')
bimapR

infixr 2 `sumR`

-- | Extend subtype relation over the 3-tuple types '(a,b,c)'
prod3R :: Sub a a' -> Sub b b' -> Sub c c' -> Sub (a,b,c) (a',b',c')
prod3R :: forall a a' b b' c c'.
Sub a a' -> Sub b b' -> Sub c c' -> Sub (a, b, c) (a', b', c')
prod3R (Sub Coercion a a'
Coercion) (Sub Coercion b b'
Coercion) (Sub Coercion c c'
Coercion) = forall k (a :: k) (b :: k). Coercion a b -> Sub a b
Sub forall {k} (a :: k) (b :: k). Coercible a b => Coercion a b
Coercion

-- | Extend subtype relation over a 'Profunctor'.
dimapR :: ( forall x x' y y'.
              (Coercible x x', Coercible y y') => Coercible (t x y) (t x' y')
          , Profunctor t)
       => Sub a a' -> Sub b b' -> Sub (t a' b) (t a b')
dimapR :: forall (t :: * -> * -> *) a a' b b'.
(forall x x' y y'.
 (Coercible x x', Coercible y y') =>
 Coercible (t x y) (t x' y'),
 Profunctor t) =>
Sub a a' -> Sub b b' -> Sub (t a' b) (t a b')
dimapR (Sub Coercion a a'
Coercion) (Sub Coercion b b'
Coercion) = forall k (a :: k) (b :: k). Coercion a b -> Sub a b
Sub forall {k} (a :: k) (b :: k). Coercible a b => Coercion a b
Coercion

-- | 'dimapR' specialized for functions @(->)@
arrR :: Sub a a' -> Sub b b' -> Sub (a' -> b) (a -> b')
arrR :: forall a a' b b'. Sub a a' -> Sub b b' -> Sub (a' -> b) (a -> b')
arrR = forall (t :: * -> * -> *) a a' b b'.
(forall x x' y y'.
 (Coercible x x', Coercible y y') =>
 Coercible (t x y) (t x' y'),
 Profunctor t) =>
Sub a a' -> Sub b b' -> Sub (t a' b) (t a b')
dimapR

infixr 1 `arrR`