    {-# LANGUAGE MonoLocalBinds #-}
    {-# LANGUAGE ConstraintKinds #-}
    {-# LANGUAGE AllowAmbiguousTypes   #-}
    {-# LANGUAGE DataKinds             #-}
    {-# LANGUAGE FlexibleContexts      #-}
    {-# LANGUAGE FlexibleInstances     #-}
    {-# LANGUAGE KindSignatures        #-}
    {-# LANGUAGE MultiParamTypeClasses #-}
    {-# LANGUAGE PolyKinds             #-}
    {-# LANGUAGE ScopedTypeVariables   #-}
    {-# LANGUAGE TypeApplications      #-}
    {-# LANGUAGE TypeOperators         #-}
    {-# LANGUAGE TypeSynonymInstances  #-}
    {-# LANGUAGE UndecidableInstances  #-}

    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Data.Generics.Sum.Internal.Subtype
    -- Copyright   :  (C) 2020 Csongor Kiss
    -- License     :  BSD3
    -- Maintainer  :  Csongor Kiss <kiss.csongor.kiss@gmail.com>
    -- Stability   :  experimental
    -- Portability :  non-portable
    --
    -- Structural subtype relationships between sum types.
    --
    -----------------------------------------------------------------------------

    module Data.Generics.Sum.Internal.Subtype
      ( Context
      , derived
      ) where

    import Data.Generics.Product.Internal.HList
    import Data.Generics.Sum.Internal.Typed (GAsType (..))

    import Data.Kind
    import GHC.Generics
    import Data.Generics.Internal.Profunctor.Iso
    import Data.Generics.Internal.Profunctor.Prism
    import Data.Generics.Internal.Families.Has

    type Context sub sup
      = ( Generic sub
        , Generic sup
        , GAsSubtype (Rep sub) (Rep sup)
        )

    derived :: Context sub sup => Prism' sup sub
    derived :: forall sub sup. Context sub sup => Prism' sup sub
    derived = p i (Rep sup (ZonkAny 0)) (Rep sup (ZonkAny 0)) -> p i sup sup
    forall a b x. (Generic a, Generic b) => Iso a b (Rep a x) (Rep b x)
    Iso sup sup (Rep sup (ZonkAny 0)) (Rep sup (ZonkAny 0))
    repIso (p i (Rep sup (ZonkAny 0)) (Rep sup (ZonkAny 0)) -> p i sup sup)
    -> (p i sub sub -> p i (Rep sup (ZonkAny 0)) (Rep sup (ZonkAny 0)))
    -> p i sub sub
    -> p i sup sup
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . p i (Rep sub (ZonkAny 0)) (Rep sub (ZonkAny 0))
    -> p i (Rep sup (ZonkAny 0)) (Rep sup (ZonkAny 0))
    forall x. Prism' (Rep sup x) (Rep sub x)
    forall (subf :: * -> *) (supf :: * -> *) x.
    GAsSubtype subf supf =>
    Prism' (supf x) (subf x)
    _GSub (p i (Rep sub (ZonkAny 0)) (Rep sub (ZonkAny 0))
     -> p i (Rep sup (ZonkAny 0)) (Rep sup (ZonkAny 0)))
    -> (p i sub sub -> p i (Rep sub (ZonkAny 0)) (Rep sub (ZonkAny 0)))
    -> p i sub sub
    -> p i (Rep sup (ZonkAny 0)) (Rep sup (ZonkAny 0))
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Iso sub sub (Rep sub (ZonkAny 0)) (Rep sub (ZonkAny 0))
    -> Iso (Rep sub (ZonkAny 0)) (Rep sub (ZonkAny 0)) sub sub
    forall s t a b. Iso s t a b -> Iso b a t s
    fromIso p i (Rep sub (ZonkAny 0)) (Rep sub (ZonkAny 0)) -> p i sub sub
    forall a b x. (Generic a, Generic b) => Iso a b (Rep a x) (Rep b x)
    Iso sub sub (Rep sub (ZonkAny 0)) (Rep sub (ZonkAny 0))
    repIso
    {-# INLINE derived #-}

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

    -- |As 'AsSubtype' but over generic representations as defined by
    --  "GHC.Generics".
    class GAsSubtype (subf :: Type -> Type) (supf :: Type -> Type) where
      _GSub :: Prism' (supf x) (subf x)

    instance
      ( GSplash sub sup
      , GDowncast sub sup
      ) => GAsSubtype sub sup where
      _GSub :: forall x. Prism' (sup x) (sub x)
    _GSub p i (sub x) (sub x)
    f = (sub x -> sup x)
    -> (sup x -> Either (sup x) (sub x))
    -> Prism (sup x) (sup x) (sub x) (sub x)
    forall b t s a. (b -> t) -> (s -> Either t a) -> Prism s t a b
    prism sub x -> sup x
    forall x. sub x -> sup x
    forall (sub :: * -> *) (sup :: * -> *) x.
    GSplash sub sup =>
    sub x -> sup x
    _GSplash sup x -> Either (sup x) (sub x)
    forall x. sup x -> Either (sup x) (sub x)
    forall {k} (sub :: k -> *) (sup :: k -> *) (x :: k).
    GDowncast sub sup =>
    sup x -> Either (sup x) (sub x)
    _GDowncast p i (sub x) (sub x)
    f
      {-# INLINE _GSub #-}

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

    class GSplash (sub :: Type -> Type) (sup :: Type -> Type) where
      _GSplash :: sub x -> sup x

    instance (GSplash a sup, GSplash b sup) => GSplash (a :+: b) sup where
      _GSplash :: forall x. (:+:) a b x -> sup x
    _GSplash (L1 a x
    rep) = a x -> sup x
    forall x. a x -> sup x
    forall (sub :: * -> *) (sup :: * -> *) x.
    GSplash sub sup =>
    sub x -> sup x
    _GSplash a x
    rep
      _GSplash (R1 b x
    rep) = b x -> sup x
    forall x. b x -> sup x
    forall (sub :: * -> *) (sup :: * -> *) x.
    GSplash sub sup =>
    sub x -> sup x
    _GSplash b x
    rep
      {-# INLINE _GSplash #-}

    instance
      ( GIsList subf subf as as
      , ListTuple as' as' as as
      , GAsType supf as'
      ) => GSplash (C1 meta subf) supf where
      _GSplash :: forall x. C1 meta subf x -> supf x
    _GSplash C1 meta subf x
    p = (Tagged (ZonkAny 2) (C1 meta subf x) (C1 meta subf x)
     -> Tagged (ZonkAny 2) (supf x) (supf x))
    -> C1 meta subf x -> supf x
    forall i b t. (Tagged i b b -> Tagged i t t) -> b -> t
    build (Tagged (ZonkAny 2) as' as' -> Tagged (ZonkAny 2) (supf x) (supf x)
    forall x. Prism (supf x) (supf x) as' as'
    forall (f :: * -> *) as x. GAsType f as => Prism (f x) (f x) as as
    _GTyped (Tagged (ZonkAny 2) as' as'
     -> Tagged (ZonkAny 2) (supf x) (supf x))
    -> (Tagged (ZonkAny 2) (C1 meta subf x) (C1 meta subf x)
        -> Tagged (ZonkAny 2) as' as')
    -> Tagged (ZonkAny 2) (C1 meta subf x) (C1 meta subf x)
    -> Tagged (ZonkAny 2) (supf x) (supf x)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Iso (C1 meta subf x) (C1 meta subf x) as' as'
    -> Iso as' as' (C1 meta subf x) (C1 meta subf x)
    forall s t a b. Iso s t a b -> Iso b a t s
    fromIso (p i (subf x) (subf x) -> p i (C1 meta subf x) (C1 meta subf x)
    forall i1 (c :: Meta) (f :: * -> *) p1 (g :: * -> *)
           (p2 :: * -> * -> * -> *) i2.
    Profunctor p2 =>
    p2 i2 (f p1) (g p1) -> p2 i2 (M1 i1 c f p1) (M1 i1 c g p1)
    mIso (p i (subf x) (subf x) -> p i (C1 meta subf x) (C1 meta subf x))
    -> (p i as' as' -> p i (subf x) (subf x))
    -> p i as' as'
    -> p i (C1 meta subf x) (C1 meta subf x)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . p i (HList as) (HList as) -> p i (subf x) (subf x)
    forall x. Iso (subf x) (subf x) (HList as) (HList as)
    forall (f :: * -> *) (g :: * -> *) (as :: [*]) (bs :: [*]) x.
    GIsList f g as bs =>
    Iso (f x) (g x) (HList as) (HList bs)
    glist (p i (HList as) (HList as) -> p i (subf x) (subf x))
    -> (p i as' as' -> p i (HList as) (HList as))
    -> p i as' as'
    -> p i (subf x) (subf x)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . p i as' as' -> p i (HList as) (HList as)
    forall tuple tuple' (as :: [*]) (bs :: [*]).
    ListTuple tuple tuple' as bs =>
    Iso (HList as) (HList bs) tuple tuple'
    Iso (HList as) (HList as) as' as'
    tupled)) C1 meta subf x
    p
      {-# INLINE _GSplash #-}

    instance GSplash sub sup => GSplash (D1 c sub) sup where
      _GSplash :: forall x. D1 c sub x -> sup x
    _GSplash (M1 sub x
    m) = sub x -> sup x
    forall x. sub x -> sup x
    forall (sub :: * -> *) (sup :: * -> *) x.
    GSplash sub sup =>
    sub x -> sup x
    _GSplash sub x
    m
      {-# INLINE _GSplash #-}

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

    class GDowncast sub sup where
      _GDowncast :: sup x -> Either (sup x) (sub x)

    instance
      ( GIsList sup sup as as
      , GDowncastC (HasPartialTypeP as sub) sub sup
      ) => GDowncast sub (C1 m sup) where
      _GDowncast :: forall x. C1 m sup x -> Either (C1 m sup x) (sub x)
    _GDowncast (M1 sup x
    m) = case forall (contains :: Bool) (sub :: * -> *) (sup :: * -> *) x.
    GDowncastC contains sub sup =>
    sup x -> Either (sup x) (sub x)
    forall {k} (contains :: Bool) (sub :: k -> *) (sup :: k -> *)
           (x :: k).
    GDowncastC contains sub sup =>
    sup x -> Either (sup x) (sub x)
    _GDowncastC @(HasPartialTypeP as sub) sup x
    m of
        Left sup x
    _ -> M1 C m sup x -> Either (M1 C m sup x) (sub x)
    forall a b. a -> Either a b
    Left (sup x -> M1 C m sup x
    forall k i (c :: Meta) (f :: k -> *) (p :: k). f p -> M1 i c f p
    M1 sup x
    m)
        Right sub x
    r -> sub x -> Either (M1 C m sup x) (sub x)
    forall a b. b -> Either a b
    Right sub x
    r
      {-# INLINE _GDowncast #-}

    instance (GDowncast sub l, GDowncast sub r) => GDowncast sub (l :+: r) where
      _GDowncast :: forall (x :: k). (:+:) l r x -> Either ((:+:) l r x) (sub x)
    _GDowncast (L1 l x
    x) = case l x -> Either (l x) (sub x)
    forall (x :: k). l x -> Either (l x) (sub x)
    forall {k} (sub :: k -> *) (sup :: k -> *) (x :: k).
    GDowncast sub sup =>
    sup x -> Either (sup x) (sub x)
    _GDowncast l x
    x of
        Left l x
    _ -> (:+:) l r x -> Either ((:+:) l r x) (sub x)
    forall a b. a -> Either a b
    Left (l x -> (:+:) l r x
    forall k (f :: k -> *) (g :: k -> *) (p :: k). f p -> (:+:) f g p
    L1 l x
    x)
        Right sub x
    r -> sub x -> Either ((:+:) l r x) (sub x)
    forall a b. b -> Either a b
    Right sub x
    r
      _GDowncast (R1 r x
    x) = case r x -> Either (r x) (sub x)
    forall (x :: k). r x -> Either (r x) (sub x)
    forall {k} (sub :: k -> *) (sup :: k -> *) (x :: k).
    GDowncast sub sup =>
    sup x -> Either (sup x) (sub x)
    _GDowncast r x
    x of
        Left r x
    _ -> (:+:) l r x -> Either ((:+:) l r x) (sub x)
    forall a b. a -> Either a b
    Left (r x -> (:+:) l r x
    forall k (f :: k -> *) (g :: k -> *) (p :: k). g p -> (:+:) f g p
    R1 r x
    x)
        Right sub x
    r -> sub x -> Either ((:+:) l r x) (sub x)
    forall a b. b -> Either a b
    Right sub x
    r
      {-# INLINE _GDowncast #-}

    instance GDowncast sub sup => GDowncast sub (D1 m sup) where
      _GDowncast :: forall (x :: k). D1 m sup x -> Either (D1 m sup x) (sub x)
    _GDowncast (M1 sup x
    m) = case sup x -> Either (sup x) (sub x)
    forall (x :: k). sup x -> Either (sup x) (sub x)
    forall {k} (sub :: k -> *) (sup :: k -> *) (x :: k).
    GDowncast sub sup =>
    sup x -> Either (sup x) (sub x)
    _GDowncast sup x
    m of
        Left sup x
    _ -> M1 D m sup x -> Either (M1 D m sup x) (sub x)
    forall a b. a -> Either a b
    Left (sup x -> M1 D m sup x
    forall k i (c :: Meta) (f :: k -> *) (p :: k). f p -> M1 i c f p
    M1 sup x
    m)
        Right sub x
    r -> sub x -> Either (M1 D m sup x) (sub x)
    forall a b. b -> Either a b
    Right sub x
    r
      {-# INLINE _GDowncast #-}

    class GDowncastC (contains :: Bool) sub sup where
      _GDowncastC :: sup x -> Either (sup x) (sub x)

    instance GDowncastC 'False sub sup where
      _GDowncastC :: forall (x :: k). sup x -> Either (sup x) (sub x)
    _GDowncastC sup x
    sup = sup x -> Either (sup x) (sub x)
    forall a b. a -> Either a b
    Left sup x
    sup
      {-# INLINE _GDowncastC #-}

    instance
      ( GAsType sub subl'
      , GIsList sup sup subl subl
      , ListTuple subl' subl' subl subl
      ) => GDowncastC 'True sub sup where
      _GDowncastC :: forall x. sup x -> Either (sup x) (sub x)
    _GDowncastC sup x
    sup = sub x -> Either (sup x) (sub x)
    forall a b. b -> Either a b
    Right ((Tagged (ZonkAny 1) (sup x) (sup x)
     -> Tagged (ZonkAny 1) (sub x) (sub x))
    -> sup x -> sub x
    forall i b t. (Tagged i b b -> Tagged i t t) -> b -> t
    build (Tagged (ZonkAny 1) subl' subl'
    -> Tagged (ZonkAny 1) (sub x) (sub x)
    forall x. Prism (sub x) (sub x) subl' subl'
    forall (f :: * -> *) as x. GAsType f as => Prism (f x) (f x) as as
    _GTyped (Tagged (ZonkAny 1) subl' subl'
     -> Tagged (ZonkAny 1) (sub x) (sub x))
    -> (Tagged (ZonkAny 1) (sup x) (sup x)
        -> Tagged (ZonkAny 1) subl' subl')
    -> Tagged (ZonkAny 1) (sup x) (sup x)
    -> Tagged (ZonkAny 1) (sub x) (sub x)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . Iso (sup x) (sup x) subl' subl' -> Iso subl' subl' (sup x) (sup x)
    forall s t a b. Iso s t a b -> Iso b a t s
    fromIso (p i (HList subl) (HList subl) -> p i (sup x) (sup x)
    forall x. Iso (sup x) (sup x) (HList subl) (HList subl)
    forall (f :: * -> *) (g :: * -> *) (as :: [*]) (bs :: [*]) x.
    GIsList f g as bs =>
    Iso (f x) (g x) (HList as) (HList bs)
    glist (p i (HList subl) (HList subl) -> p i (sup x) (sup x))
    -> (p i subl' subl' -> p i (HList subl) (HList subl))
    -> p i subl' subl'
    -> p i (sup x) (sup x)
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . p i subl' subl' -> p i (HList subl) (HList subl)
    forall tuple tuple' (as :: [*]) (bs :: [*]).
    ListTuple tuple tuple' as bs =>
    Iso (HList as) (HList bs) tuple tuple'
    Iso (HList subl) (HList subl) subl' subl'
    tupled)) sup x
    sup)
      {-# INLINE _GDowncastC #-}