    {-# LANGUAGE CPP #-}
    {-# LANGUAGE DataKinds #-}
    {-# LANGUAGE DeriveFunctor #-}
    {-# LANGUAGE EmptyDataDecls #-}
    {-# LANGUAGE FlexibleInstances #-}
    {-# LANGUAGE FunctionalDependencies #-}
    {-# LANGUAGE KindSignatures #-}
    {-# LANGUAGE NoImplicitPrelude #-}
    {-# LANGUAGE ScopedTypeVariables #-}
    {-# LANGUAGE TypeOperators #-}
    {-# LANGUAGE UndecidableInstances #-}

    #include "overlapping-compat.h"

    -- |
    -- Module:      Data.Aeson.Types.Generic
    -- Copyright:   (c) 2012-2016 Bryan O'Sullivan
    --              (c) 2011, 2012 Bas Van Dijk
    --              (c) 2011 MailRank, Inc.
    -- License:     BSD3
    -- Maintainer:  Bryan O'Sullivan <bos@serpentine.com>
    -- Stability:   experimental
    -- Portability: portable
    --
    -- Helpers for generic derivations.

    module Data.Aeson.Types.Generic
        (
          IsRecord
        , AllNullary
        , Tagged2(..)
        , True
        , False
        , And
        , Zero
        , One
        , ProductSize(..)
        , (:*)(..)
        ) where

    import Prelude.Compat

    import GHC.Generics

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

    class IsRecord (f :: * -> *) isRecord | f -> isRecord

    instance (IsRecord f isRecord) => IsRecord (f :*: g) isRecord
    #if MIN_VERSION_base(4,9,0)
    instance OVERLAPPING_ IsRecord (M1 S ('MetaSel 'Nothing u ss ds) f) False
    #else
    instance OVERLAPPING_ IsRecord (M1 S NoSelector f) False
    #endif
    instance (IsRecord f isRecord) => IsRecord (M1 S c f) isRecord
    instance IsRecord (K1 i c) True
    instance IsRecord Par1 True
    instance IsRecord (Rec1 f) True
    instance IsRecord (f :.: g) True
    instance IsRecord U1 False

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

    class AllNullary (f :: * -> *) allNullary | f -> allNullary

    instance ( AllNullary a allNullaryL
             , AllNullary b allNullaryR
             , And allNullaryL allNullaryR allNullary
             ) => AllNullary (a :+: b) allNullary
    instance AllNullary a allNullary => AllNullary (M1 i c a) allNullary
    instance AllNullary (a :*: b) False
    instance AllNullary (a :.: b) False
    instance AllNullary (K1 i c) False
    instance AllNullary Par1 False
    instance AllNullary (Rec1 f) False
    instance AllNullary U1 True

    newtype Tagged2 (s :: * -> *) b = Tagged2 {Tagged2 s b -> b
    unTagged2 :: b}
      deriving a -> Tagged2 s b -> Tagged2 s a
    (a -> b) -> Tagged2 s a -> Tagged2 s b
    (forall a b. (a -> b) -> Tagged2 s a -> Tagged2 s b)
    -> (forall a b. a -> Tagged2 s b -> Tagged2 s a)
    -> Functor (Tagged2 s)
    forall a b. a -> Tagged2 s b -> Tagged2 s a
    forall a b. (a -> b) -> Tagged2 s a -> Tagged2 s b
    forall (f :: * -> *).
    (forall a b. (a -> b) -> f a -> f b)
    -> (forall a b. a -> f b -> f a) -> Functor f
    forall (s :: * -> *) a b. a -> Tagged2 s b -> Tagged2 s a
    forall (s :: * -> *) a b. (a -> b) -> Tagged2 s a -> Tagged2 s b
    <$ :: a -> Tagged2 s b -> Tagged2 s a
    $c<$ :: forall (s :: * -> *) a b. a -> Tagged2 s b -> Tagged2 s a
    fmap :: (a -> b) -> Tagged2 s a -> Tagged2 s b
    $cfmap :: forall (s :: * -> *) a b. (a -> b) -> Tagged2 s a -> Tagged2 s b
    Functor

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

    data True
    data False

    class    And bool1 bool2 bool3 | bool1 bool2 -> bool3

    instance And True  True  True
    instance And False False False
    instance And False True  False
    instance And True  False False

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

    -- | A type-level indicator that 'ToJSON' or 'FromJSON' is being derived generically.
    data Zero

    -- | A type-level indicator that 'ToJSON1' or 'FromJSON1' is being derived generically.
    data One

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

    class ProductSize f where
        productSize :: Tagged2 f Int

    instance (ProductSize a, ProductSize b) => ProductSize (a :*: b) where
        productSize :: Tagged2 (a :*: b) Int
    productSize = Int -> Tagged2 (a :*: b) Int
    forall (s :: * -> *) b. b -> Tagged2 s b
    Tagged2 (Int -> Tagged2 (a :*: b) Int) -> Int -> Tagged2 (a :*: b) Int
    forall a b. (a -> b) -> a -> b
    $ Tagged2 a Int -> Int
    forall (s :: * -> *) b. Tagged2 s b -> b
    unTagged2 (Tagged2 a Int
    forall (f :: * -> *). ProductSize f => Tagged2 f Int
    productSize :: Tagged2 a Int) Int -> Int -> Int
    forall a. Num a => a -> a -> a
    +
                                Tagged2 b Int -> Int
    forall (s :: * -> *) b. Tagged2 s b -> b
    unTagged2 (Tagged2 b Int
    forall (f :: * -> *). ProductSize f => Tagged2 f Int
    productSize :: Tagged2 b Int)

    instance ProductSize (S1 s a) where
        productSize :: Tagged2 (S1 s a) Int
    productSize = Int -> Tagged2 (S1 s a) Int
    forall (s :: * -> *) b. b -> Tagged2 s b
    Tagged2 1

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

    -- | Simple extensible tuple type to simplify passing around many parameters.
    data a :* b = a :* b

    infixr 1 :*