    {-# LANGUAGE CPP #-}
    {-# LANGUAGE FlexibleContexts #-}
    {-# LANGUAGE FlexibleInstances #-}
    {-# LANGUAGE TypeOperators #-}
    {-# LANGUAGE TypeSynonymInstances #-}

    #if __GLASGOW_HASKELL__ >= 701
    {-# LANGUAGE DefaultSignatures #-}
    {-# LANGUAGE Safe #-}
    #endif

    #if __GLASGOW_HASKELL__ >= 705
    {-# LANGUAGE PolyKinds #-}
    #endif

    module Generics.Deriving.Functor (
      -- * Generic Functor class
        GFunctor(..)

      -- * Default method
      , gmapdefault

      -- * Internal Functor class
      , GFunctor'(..)

      ) where

    import           Control.Applicative (Const, ZipList)

    import qualified Data.Monoid as Monoid (First, Last, Product, Sum)
    import           Data.Monoid (Dual)

    import           Generics.Deriving.Base

    #if MIN_VERSION_base(4,4,0)
    import           Data.Complex (Complex)
    #endif

    #if MIN_VERSION_base(4,7,0)
    import           Data.Proxy (Proxy)
    #endif

    #if MIN_VERSION_base(4,8,0)
    import           Data.Functor.Identity (Identity)
    import           Data.Monoid (Alt)
    #endif

    #if MIN_VERSION_base(4,9,0)
    import qualified Data.Functor.Product as Functor (Product)
    import qualified Data.Functor.Sum as Functor (Sum)
    import           Data.List.NonEmpty (NonEmpty)
    import qualified Data.Semigroup as Semigroup (First, Last)
    import           Data.Semigroup (Arg, Max, Min, Option, WrappedMonoid)
    #endif

    --------------------------------------------------------------------------------
    -- Generic fmap
    --------------------------------------------------------------------------------

    class GFunctor' f where
      gmap' :: (a -> b) -> f a -> f b

    instance GFunctor' U1 where
      gmap' _ U1 = U1

    instance GFunctor' Par1 where
      gmap' f (Par1 a) = Par1 (f a)

    instance GFunctor' (K1 i c) where
      gmap' _ (K1 a) = K1 a

    instance (GFunctor f) => GFunctor' (Rec1 f) where
      gmap' f (Rec1 a) = Rec1 (gmap f a)

    instance (GFunctor' f) => GFunctor' (M1 i c f) where
      gmap' f (M1 a) = M1 (gmap' f a)

    instance (GFunctor' f, GFunctor' g) => GFunctor' (f :+: g) where
      gmap' f (L1 a) = L1 (gmap' f a)
      gmap' f (R1 a) = R1 (gmap' f a)

    instance (GFunctor' f, GFunctor' g) => GFunctor' (f :*: g) where
      gmap' f (a :*: b) = gmap' f a :*: gmap' f b

    instance (GFunctor f, GFunctor' g) => GFunctor' (f :.: g) where
      gmap' f (Comp1 x) = Comp1 (gmap (gmap' f) x)

    instance GFunctor' UAddr where
      gmap' _ (UAddr a) = UAddr a

    instance GFunctor' UChar where
      gmap' _ (UChar c) = UChar c

    instance GFunctor' UDouble where
      gmap' _ (UDouble d) = UDouble d

    instance GFunctor' UFloat where
      gmap' _ (UFloat f) = UFloat f

    instance GFunctor' UInt where
      gmap' _ (UInt i) = UInt i

    instance GFunctor' UWord where
      gmap' _ (UWord w) = UWord w

    class GFunctor f where
      gmap :: (a -> b) -> f a -> f b
    #if __GLASGOW_HASKELL__ >= 701
      default gmap :: (Generic1 f, GFunctor' (Rep1 f))
                   => (a -> b) -> f a -> f b
      gmap = gmapdefault
    #endif

    gmapdefault :: (Generic1 f, GFunctor' (Rep1 f))
                => (a -> b) -> f a -> f b
    gmapdefault f = to1 . gmap' f . from1

    -- Base types instances
    instance GFunctor ((->) r) where
      gmap = fmap

    instance GFunctor ((,) a) where
      gmap = gmapdefault

    instance GFunctor [] where
      gmap = gmapdefault

    #if MIN_VERSION_base(4,8,0)
    instance GFunctor f => GFunctor (Alt f) where
      gmap = gmapdefault
    #endif

    #if MIN_VERSION_base(4,9,0)
    instance GFunctor (Arg a) where
      gmap = gmapdefault
    #endif

    #if MIN_VERSION_base(4,4,0)
    instance GFunctor Complex where
      gmap = gmapdefault
    #endif

    instance GFunctor (Const m) where
      gmap = gmapdefault

    instance GFunctor Dual where
      gmap = gmapdefault

    instance GFunctor (Either a) where
      gmap = gmapdefault

    instance GFunctor Monoid.First where
      gmap = gmapdefault

    #if MIN_VERSION_base(4,9,0)
    instance GFunctor (Semigroup.First) where
      gmap = gmapdefault
    #endif

    #if MIN_VERSION_base(4,8,0)
    instance GFunctor Identity where
      gmap = gmapdefault
    #endif

    instance GFunctor IO where
      gmap = fmap

    instance GFunctor Monoid.Last where
      gmap = gmapdefault

    #if MIN_VERSION_base(4,9,0)
    instance GFunctor Semigroup.Last where
      gmap = gmapdefault

    instance GFunctor Max where
      gmap = gmapdefault
    #endif

    instance GFunctor Maybe where
      gmap = gmapdefault

    #if MIN_VERSION_base(4,9,0)
    instance GFunctor Min where
      gmap = gmapdefault

    instance GFunctor NonEmpty where
      gmap = gmapdefault

    instance GFunctor Option where
      gmap = gmapdefault
    #endif

    instance GFunctor Monoid.Product where
      gmap = gmapdefault

    #if MIN_VERSION_base(4,9,0)
    instance (GFunctor f, GFunctor g) => GFunctor (Functor.Product f g) where
      gmap = gmapdefault
    #endif

    #if MIN_VERSION_base(4,7,0)
    instance GFunctor Proxy where
      gmap = gmapdefault
    #endif

    instance GFunctor Monoid.Sum where
      gmap = gmapdefault

    #if MIN_VERSION_base(4,9,0)
    instance (GFunctor f, GFunctor g) => GFunctor (Functor.Sum f g) where
      gmap = gmapdefault

    instance GFunctor WrappedMonoid where
      gmap = gmapdefault
    #endif

    instance GFunctor ZipList where
      gmap = gmapdefault