    {-# LANGUAGE CPP #-}
    {-# LANGUAGE TypeOperators #-}
    {-# LANGUAGE FlexibleContexts #-}
    -----------------------------------------------------------------------------
    -- |
    -- Module      :  Data.Distributive
    -- Copyright   :  (C) 2011-2014 Edward Kmett
    -- License     :  BSD-style (see the file LICENSE)
    --
    -- Maintainer  :  Edward Kmett <ekmett@gmail.com>
    -- Stability   :  provisional
    -- Portability :  portable
    --
    ----------------------------------------------------------------------------
    module Data.Distributive.Generic
      ( GDistributive(..)
      , genericDistribute
      ) where

    import GHC.Generics

    -- | 'distribute' derived from a 'Generic1' type
    --
    -- This can be used to easily produce a 'Distributive' instance for a
    -- type with a 'Generic1' instance,
    --
    -- > data V2 a = V2 a a deriving (Show, Functor, Generic1)
    -- > instance Distributive V2' where distribute = genericDistribute
    genericDistribute  :: (Functor f, Generic1 g, GDistributive (Rep1 g)) => f (g a) -> g (f a)
    genericDistribute = to1 . gdistribute . fmap from1

    -- Can't distribute over,
    --   * sums (:+:)
    --   * K1
    class GDistributive g where
      gdistribute :: Functor f => f (g a) -> g (f a)

    instance GDistributive U1 where
      gdistribute _ = U1
      {-# INLINE gdistribute #-}

    instance (GDistributive a, GDistributive b) => GDistributive (a :*: b) where
      gdistribute f = gdistribute (fmap fstP f) :*: gdistribute (fmap sndP f) where
        fstP (l :*: _) = l
        sndP (_ :*: r) = r
      {-# INLINE gdistribute #-}

    instance (Functor a, Functor b, GDistributive a, GDistributive b) => GDistributive (a :.: b) where
      gdistribute = Comp1 . fmap gdistribute . gdistribute . fmap unComp1
      {-# INLINE gdistribute #-}

    instance GDistributive Par1 where
      gdistribute = Par1 . fmap unPar1
      {-# INLINE gdistribute #-}

    instance GDistributive f => GDistributive (Rec1 f) where
      gdistribute = Rec1 . gdistribute . fmap unRec1
      {-# INLINE gdistribute #-}

    instance GDistributive f => GDistributive (M1 i c f) where
      gdistribute = M1 . gdistribute . fmap unM1
      {-# INLINE gdistribute #-}