content: {-# LANGUAGE AllowAmbiguousTypes #-}
  {-# LANGUAGE TypeApplications #-}
  {-# language ConstraintKinds #-}
  {-# language DataKinds #-}
  {-# language FlexibleContexts #-}
  {-# language FlexibleInstances #-}
  {-# language FunctionalDependencies #-}
  {-# language MultiParamTypeClasses #-}
  {-# language ScopedTypeVariables #-}
  {-# language TypeFamilies #-}
  {-# language TypeOperators #-}
  {-# language UndecidableInstances #-}
  
  module Data.Generics.Internal.Wrapped
    ( Context
    , derived
    ) where
  
  import Data.Generics.Internal.Profunctor.Iso
  
  import Data.Generics.Internal.Families.Changing ( UnifyHead )
  
  import Data.Kind (Constraint)
  import GHC.Generics
  import GHC.TypeLits
  
  type Context s t a b
    = ( Generic s
      , Generic t
      , GWrapped (Rep s) (Rep t) a b
      , UnifyHead s t
      , UnifyHead t s
      , ErrorUnlessOnlyOne s (Rep s)
      )
  
  derived :: Context s t a b => Iso s t a b
  derived = repIso . gWrapped
  {-# INLINE derived #-}
  
  type family ErrorUnlessOnlyOne a b :: Constraint where
    ErrorUnlessOnlyOne t (M1 i k a) = ErrorUnlessOnlyOne t a
    ErrorUnlessOnlyOne t (K1 i a) = ()
    ErrorUnlessOnlyOne t a =
      TypeError ('ShowType t ':<>: 'Text " is not a single-constructor, single-field datatype")
  
  --------------------------------------------------------------------------------
  
  class GWrapped s t a b | s -> a, t -> b, s b -> t, t a -> s where
    gWrapped :: Iso (s x) (t x) a b
  
  instance GWrapped s t a b => GWrapped (M1 i k s) (M1 i k t) a b where
    gWrapped = mIso . gWrapped
  
  instance (a ~ c, b ~ d) => GWrapped (K1 i a) (K1 i b) c d where
    gWrapped = kIso
  