module Data.Functor.Module
  ( -- * LeftModule
    LeftModule (..),

    -- * RightModule
    RightModule (..),

    -- * Bimodule
    Bimodule,
  )
where

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

import Control.Applicative (ZipList)
import Data.Either (Either (..))
import Data.Functor (Functor (..))
import Data.Functor.Compose (Compose)
import Data.Functor.Const (Const)
import Data.Functor.Contravariant (Comparison, Contravariant (..), Equivalence, Op (Op), Predicate)
import Data.Functor.Identity (Identity)
import Data.Functor.Product (Product)
import Data.Functor.Sum (Sum)
import Data.List.NonEmpty (NonEmpty)
import Data.Maybe (Maybe)
import Data.Monoid (Alt)
import Data.Proxy (Proxy)
import Data.These (These (..))
import Data.Tuple (fst, snd)
import GHC.Generics (K1, M1, Rec1, U1, V1, type (:*:), type (:+:), type (:.:))
import Prelude (IO)

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

newtype FromContra f a = FromContra (f a)
  deriving newtype (Contravariant)

newtype FromFunctor f a = FromFunctor (f a)
  deriving newtype (Functor)

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

class LeftModule cat t1 f where
  -- | ==== __Examples__
  --
  -- >>> :t lstrength @(->) @(,) @Predicate (Predicate @Int (> 10))
  -- lstrength @(->) @(,) @Predicate (Predicate @Int (> 10)) :: Predicate (Int, x)
  --
  -- >>> :t lstrength @(->) @Either @[] 
  -- lstrength @(->) @Either @[] :: a -> [Either a x]
  --
  -- >>> lstrength @(->) @Either @[] [True, False]
  -- [Left True,Left False]
  lstrength :: cat (f a) (f (t1 a x))

instance Contravariant f => LeftModule (->) (,) (FromContra f) where
  lstrength :: FromContra f a -> FromContra f (a, x)
  lstrength = contramap fst

instance Contravariant f => LeftModule Op Either (FromContra f) where
  lstrength = Op (contramap Left)

instance Functor f => LeftModule (->) Either (FromFunctor f) where
  lstrength :: FromFunctor f a -> FromFunctor f (Either a x)
  lstrength = fmap Left

instance Functor f => LeftModule (->) These (FromFunctor f) where
  lstrength :: FromFunctor f a -> FromFunctor f (These a x)
  lstrength = fmap This

instance Functor f => LeftModule Op (,) (FromFunctor f) where
  lstrength = Op (fmap fst)

deriving via (FromContra Comparison)    instance LeftModule (->) (,) Comparison
deriving via (FromContra Equivalence)   instance LeftModule (->) (,) Equivalence
deriving via (FromContra Predicate)     instance LeftModule (->) (,) Predicate
deriving via (FromContra (Op a))        instance LeftModule (->) (,) (Op a)
deriving via (FromContra Proxy)         instance LeftModule (->) (,) Proxy
deriving via (FromContra U1)            instance LeftModule (->) (,) U1
deriving via (FromContra V1)            instance LeftModule (->) (,) V1
deriving via (FromContra (Const a))     instance LeftModule (->) (,) (Const a)
deriving via (FromContra (Alt f))       instance Contravariant f => LeftModule (->) (,) (Alt f)
deriving via (FromContra (Rec1 f))      instance Contravariant f => LeftModule (->) (,) (Rec1 f)
deriving via (FromContra (Product f g)) instance (Contravariant f, Contravariant g) => LeftModule (->) (,) (Product f g)
deriving via (FromContra (Sum f g))     instance (Contravariant f, Contravariant g) => LeftModule (->) (,) (Sum f g)
deriving via (FromContra (f :+: g))     instance (Contravariant f, Contravariant g) => LeftModule (->) (,) (f :+: g)
deriving via (FromContra (f :*: g))     instance (Contravariant f, Contravariant g) => LeftModule (->) (,) (f :*: g)
deriving via (FromContra (K1 i c))      instance LeftModule (->) (,) (K1 i c)
deriving via (FromContra (Compose f g)) instance (Functor f, Contravariant g) => LeftModule (->) (,) (Compose f g)
deriving via (FromContra (f :.: g))     instance (Functor f, Contravariant g) => LeftModule (->) (,) (f :.: g)
deriving via (FromContra (M1 i c f))    instance Contravariant f => LeftModule (->) (,) (M1 i c f)

deriving via (FromContra Comparison)    instance LeftModule Op Either Comparison
deriving via (FromContra Equivalence)   instance LeftModule Op Either Equivalence
deriving via (FromContra Predicate)     instance LeftModule Op Either Predicate
deriving via (FromContra (Op a))        instance LeftModule Op Either (Op a)
deriving via (FromContra Proxy)         instance LeftModule Op Either Proxy
deriving via (FromContra U1)            instance LeftModule Op Either U1
deriving via (FromContra V1)            instance LeftModule Op Either V1
deriving via (FromContra (Const a))     instance LeftModule Op Either (Const a)
deriving via (FromContra (Alt f))       instance Contravariant f => LeftModule Op Either (Alt f)
deriving via (FromContra (Rec1 f))      instance Contravariant f => LeftModule Op Either (Rec1 f)
deriving via (FromContra (Product f g)) instance (Contravariant f, Contravariant g) => LeftModule Op Either (Product f g)
deriving via (FromContra (Sum f g))     instance (Contravariant f, Contravariant g) => LeftModule Op Either (Sum f g)
deriving via (FromContra (f :+: g))     instance (Contravariant f, Contravariant g) => LeftModule Op Either (f :+: g)
deriving via (FromContra (f :*: g))     instance (Contravariant f, Contravariant g) => LeftModule Op Either (f :*: g)
deriving via (FromContra (K1 i c))      instance LeftModule Op Either (K1 i c)
deriving via (FromContra (Compose f g)) instance (Functor f, Contravariant g) => LeftModule Op Either (Compose f g)
deriving via (FromContra (f :.: g))     instance (Functor f, Contravariant g) => LeftModule Op Either (f :.: g)
deriving via (FromContra (M1 i c f))    instance Contravariant f => LeftModule Op Either (M1 i c f)

deriving via FromFunctor Identity           instance LeftModule (->) Either Identity
deriving via FromFunctor (Compose f g)      instance (Functor f, Functor g) => LeftModule (->) Either (Compose f g)
deriving via FromFunctor []                 instance LeftModule (->) Either []
deriving via FromFunctor ZipList            instance LeftModule (->) Either ZipList
deriving via FromFunctor NonEmpty           instance LeftModule (->) Either NonEmpty
deriving via FromFunctor Maybe              instance LeftModule (->) Either Maybe
deriving via FromFunctor (Either e)         instance LeftModule (->) Either (Either e)
deriving via FromFunctor (These a)          instance LeftModule (->) Either (These a)
deriving via FromFunctor IO                 instance LeftModule (->) Either IO
deriving via FromFunctor (Sum f g)          instance (Functor f, Functor g) => LeftModule (->) Either (Sum f g)
deriving via FromFunctor (Product f g)      instance (Functor f, Functor g) => LeftModule (->) Either (Product f g)
deriving via (FromFunctor ((,) x1))         instance LeftModule (->) Either ((,) x1)
deriving via (FromFunctor ((,,) x1 x2))     instance LeftModule (->) Either ((,,) x1 x2)
deriving via (FromFunctor ((,,,) x1 x2 x3)) instance LeftModule (->) Either ((,,,) x1 x2 x3)

deriving via FromFunctor Identity           instance LeftModule (->) These Identity
deriving via FromFunctor (Compose f g)      instance (Functor f, Functor g) => LeftModule (->) These (Compose f g)
deriving via FromFunctor []                 instance LeftModule (->) These []
deriving via FromFunctor ZipList            instance LeftModule (->) These ZipList
deriving via FromFunctor NonEmpty           instance LeftModule (->) These NonEmpty
deriving via FromFunctor Maybe              instance LeftModule (->) These Maybe
deriving via FromFunctor (Either e)         instance LeftModule (->) These (Either e)
deriving via FromFunctor (These a)          instance LeftModule (->) These (These a)
deriving via FromFunctor IO                 instance LeftModule (->) These IO
deriving via FromFunctor (Sum f g)          instance (Functor f, Functor g) => LeftModule (->) These (Sum f g)
deriving via FromFunctor (Product f g)      instance (Functor f, Functor g) => LeftModule (->) These (Product f g)
deriving via (FromFunctor ((,) x1))         instance LeftModule (->) These ((,) x1)
deriving via (FromFunctor ((,,) x1 x2))     instance LeftModule (->) These ((,,) x1 x2)
deriving via (FromFunctor ((,,,) x1 x2 x3)) instance LeftModule (->) These ((,,,) x1 x2 x3)

deriving via FromFunctor Identity           instance LeftModule Op (,) Identity
deriving via FromFunctor (Compose f g)      instance (Functor f, Functor g) => LeftModule Op (,) (Compose f g)
deriving via FromFunctor []                 instance LeftModule Op (,) []
deriving via FromFunctor ZipList            instance LeftModule Op (,) ZipList
deriving via FromFunctor NonEmpty           instance LeftModule Op (,) NonEmpty
deriving via FromFunctor Maybe              instance LeftModule Op (,) Maybe
deriving via FromFunctor (Either e)         instance LeftModule Op (,) (Either e)
deriving via FromFunctor IO                 instance LeftModule Op (,) IO
deriving via FromFunctor (Sum f g)          instance (Functor f, Functor g) => LeftModule Op (,) (Sum f g)
deriving via FromFunctor (Product f g)      instance (Functor f, Functor g) => LeftModule Op (,) (Product f g)
deriving via (FromFunctor ((,) x1))         instance LeftModule Op (,) ((,) x1)
deriving via (FromFunctor ((,,) x1 x2))     instance LeftModule Op (,) ((,,) x1 x2)
deriving via (FromFunctor ((,,,) x1 x2 x3)) instance LeftModule Op (,) ((,,,) x1 x2 x3)

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

class RightModule cat t1 f where
  -- | ==== __Examples__
  --
  -- >>> :t rstrength @(->) @(,) @Predicate (Predicate @Int (> 10))
  -- rstrength @(->) @(,) @Predicate (Predicate @Int (> 10)) :: Predicate (x, Int)
  --
  -- >>> :t rstrength @(->) @Either @[] 
  -- rstrength @(->) @Either @[] :: [a] -> [Either x a]
  -- >>> rstrength @(->) @Either @[] [True, False]
  -- [Right True,Right False]
  rstrength :: cat (f a) (f (t1 x a))

instance Contravariant f => RightModule (->) (,) (FromContra f) where
  rstrength :: FromContra f a -> FromContra f (x, a)
  rstrength = contramap snd

instance Contravariant f => RightModule Op Either (FromContra f) where
  rstrength :: Op (FromContra f a) (FromContra f (Either x a))
  rstrength = Op (contramap Right)

instance Functor f => RightModule (->) Either (FromFunctor f) where
  rstrength :: FromFunctor f a -> FromFunctor f (Either x a)
  rstrength = fmap Right

instance Functor f => RightModule (->) These (FromFunctor f) where
  rstrength :: FromFunctor f a -> FromFunctor f (These x a)
  rstrength = fmap That

instance Functor f => RightModule Op (,) (FromFunctor f) where
  rstrength :: Op (FromFunctor f a) (FromFunctor f (x, a))
  rstrength = Op (fmap snd)

deriving via (FromContra Comparison)    instance RightModule (->) (,) Comparison
deriving via (FromContra Equivalence)   instance RightModule (->) (,) Equivalence
deriving via (FromContra Predicate)     instance RightModule (->) (,) Predicate
deriving via (FromContra (Op a))        instance RightModule (->) (,) (Op a)
deriving via (FromContra Proxy)         instance RightModule (->) (,) Proxy
deriving via (FromContra U1)            instance RightModule (->) (,) U1
deriving via (FromContra V1)            instance RightModule (->) (,) V1
deriving via (FromContra (Const a))     instance RightModule (->) (,) (Const a)
deriving via (FromContra (Alt f))       instance Contravariant f => RightModule (->) (,) (Alt f)
deriving via (FromContra (Rec1 f))      instance Contravariant f => RightModule (->) (,) (Rec1 f)
deriving via (FromContra (Product f g)) instance (Contravariant f, Contravariant g) => RightModule (->) (,) (Product f g)
deriving via (FromContra (Sum f g))     instance (Contravariant f, Contravariant g) => RightModule (->) (,) (Sum f g)
deriving via (FromContra (f :+: g))     instance (Contravariant f, Contravariant g) => RightModule (->) (,) (f :+: g)
deriving via (FromContra (f :*: g))     instance (Contravariant f, Contravariant g) => RightModule (->) (,) (f :*: g)
deriving via (FromContra (K1 i c))      instance RightModule (->) (,) (K1 i c)
deriving via (FromContra (Compose f g)) instance (Functor f, Contravariant g) => RightModule (->) (,) (Compose f g)
deriving via (FromContra (f :.: g))     instance (Functor f, Contravariant g) => RightModule (->) (,) (f :.: g)
deriving via (FromContra (M1 i c f))    instance Contravariant f => RightModule (->) (,) (M1 i c f)

deriving via (FromContra Comparison)    instance RightModule Op Either Comparison
deriving via (FromContra Equivalence)   instance RightModule Op Either Equivalence
deriving via (FromContra Predicate)     instance RightModule Op Either Predicate
deriving via (FromContra (Op a))        instance RightModule Op Either (Op a)
deriving via (FromContra Proxy)         instance RightModule Op Either Proxy
deriving via (FromContra U1)            instance RightModule Op Either U1
deriving via (FromContra V1)            instance RightModule Op Either V1
deriving via (FromContra (Const a))     instance RightModule Op Either (Const a)
deriving via (FromContra (Alt f))       instance Contravariant f => RightModule Op Either (Alt f)
deriving via (FromContra (Rec1 f))      instance Contravariant f => RightModule Op Either (Rec1 f)
deriving via (FromContra (Product f g)) instance (Contravariant f, Contravariant g) => RightModule Op Either (Product f g)
deriving via (FromContra (Sum f g))     instance (Contravariant f, Contravariant g) => RightModule Op Either (Sum f g)
deriving via (FromContra (f :+: g))     instance (Contravariant f, Contravariant g) => RightModule Op Either (f :+: g)
deriving via (FromContra (f :*: g))     instance (Contravariant f, Contravariant g) => RightModule Op Either (f :*: g)
deriving via (FromContra (K1 i c))      instance RightModule Op Either (K1 i c)
deriving via (FromContra (Compose f g)) instance (Functor f, Contravariant g) => RightModule Op Either (Compose f g)
deriving via (FromContra (f :.: g))     instance (Functor f, Contravariant g) => RightModule Op Either (f :.: g)
deriving via (FromContra (M1 i c f))    instance Contravariant f => RightModule Op Either (M1 i c f)

deriving via FromFunctor Identity           instance RightModule (->) Either Identity
deriving via FromFunctor (Compose f g)      instance (Functor f, Functor g) => RightModule (->) Either (Compose f g)
deriving via FromFunctor []                 instance RightModule (->) Either []
deriving via FromFunctor ZipList            instance RightModule (->) Either ZipList
deriving via FromFunctor NonEmpty           instance RightModule (->) Either NonEmpty
deriving via FromFunctor Maybe              instance RightModule (->) Either Maybe
deriving via FromFunctor (Either e)         instance RightModule (->) Either (Either e)
deriving via FromFunctor (These a)          instance RightModule (->) Either (These a)
deriving via FromFunctor IO                 instance RightModule (->) Either IO
deriving via FromFunctor (Sum f g)          instance (Functor f, Functor g) => RightModule (->) Either (Sum f g)
deriving via FromFunctor (Product f g)      instance (Functor f, Functor g) => RightModule (->) Either (Product f g)
deriving via (FromFunctor ((,) x1))         instance RightModule (->) Either ((,) x1)
deriving via (FromFunctor ((,,) x1 x2))     instance RightModule (->) Either ((,,) x1 x2)
deriving via (FromFunctor ((,,,) x1 x2 x3)) instance RightModule (->) Either ((,,,) x1 x2 x3)

deriving via FromFunctor Identity           instance RightModule (->) These Identity
deriving via FromFunctor (Compose f g)      instance (Functor f, Functor g) => RightModule (->) These (Compose f g)
deriving via FromFunctor []                 instance RightModule (->) These []
deriving via FromFunctor ZipList            instance RightModule (->) These ZipList
deriving via FromFunctor NonEmpty           instance RightModule (->) These NonEmpty
deriving via FromFunctor Maybe              instance RightModule (->) These Maybe
deriving via FromFunctor (Either e)         instance RightModule (->) These (Either e)
deriving via FromFunctor (These a)          instance RightModule (->) These (These a)
deriving via FromFunctor IO                 instance RightModule (->) These IO
deriving via FromFunctor (Sum f g)          instance (Functor f, Functor g) => RightModule (->) These (Sum f g)
deriving via FromFunctor (Product f g)      instance (Functor f, Functor g) => RightModule (->) These (Product f g)
deriving via (FromFunctor ((,) x1))         instance RightModule (->) These ((,) x1)
deriving via (FromFunctor ((,,) x1 x2))     instance RightModule (->) These ((,,) x1 x2)
deriving via (FromFunctor ((,,,) x1 x2 x3)) instance RightModule (->) These ((,,,) x1 x2 x3)

deriving via FromFunctor Identity           instance RightModule Op (,) Identity
deriving via FromFunctor (Compose f g)      instance (Functor f, Functor g) => RightModule Op (,) (Compose f g)
deriving via FromFunctor []                 instance RightModule Op (,) []
deriving via FromFunctor ZipList            instance RightModule Op (,) ZipList
deriving via FromFunctor NonEmpty           instance RightModule Op (,) NonEmpty
deriving via FromFunctor Maybe              instance RightModule Op (,) Maybe
deriving via FromFunctor (Either e)         instance RightModule Op (,) (Either e)
deriving via FromFunctor IO                 instance RightModule Op (,) IO
deriving via FromFunctor (Sum f g)          instance (Functor f, Functor g) => RightModule Op (,) (Sum f g)
deriving via FromFunctor (Product f g)      instance (Functor f, Functor g) => RightModule Op (,) (Product f g)
deriving via (FromFunctor ((,) x1))         instance RightModule Op (,) ((,) x1)
deriving via (FromFunctor ((,,) x1 x2))     instance RightModule Op (,) ((,,) x1 x2)
deriving via (FromFunctor ((,,,) x1 x2 x3)) instance RightModule Op (,) ((,,,) x1 x2 x3)

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

class (LeftModule cat t1 f, RightModule cat t1 f) => Bimodule cat t1 f

instance Contravariant f => Bimodule (->) (,) (FromContra f) where
instance Contravariant f => Bimodule Op Either (FromContra f) where
instance Functor f => Bimodule (->) Either (FromFunctor f) where
instance Functor f => Bimodule (->) These (FromFunctor f) where
instance Functor f => Bimodule Op (,) (FromFunctor f) where

deriving via (FromContra Comparison)    instance Bimodule (->) (,) Comparison
deriving via (FromContra Equivalence)   instance Bimodule (->) (,) Equivalence
deriving via (FromContra Predicate)     instance Bimodule (->) (,) Predicate
deriving via (FromContra (Op a))        instance Bimodule (->) (,) (Op a)
deriving via (FromContra Proxy)         instance Bimodule (->) (,) Proxy
deriving via (FromContra U1)            instance Bimodule (->) (,) U1
deriving via (FromContra V1)            instance Bimodule (->) (,) V1
deriving via (FromContra (Const a))     instance Bimodule (->) (,) (Const a)
deriving via (FromContra (Alt f))       instance Contravariant f => Bimodule (->) (,) (Alt f)
deriving via (FromContra (Rec1 f))      instance Contravariant f => Bimodule (->) (,) (Rec1 f)
deriving via (FromContra (Product f g)) instance (Contravariant f, Contravariant g) => Bimodule (->) (,) (Product f g)
deriving via (FromContra (Sum f g))     instance (Contravariant f, Contravariant g) => Bimodule (->) (,) (Sum f g)
deriving via (FromContra (f :+: g))     instance (Contravariant f, Contravariant g) => Bimodule (->) (,) (f :+: g)
deriving via (FromContra (f :*: g))     instance (Contravariant f, Contravariant g) => Bimodule (->) (,) (f :*: g)
deriving via (FromContra (K1 i c))      instance Bimodule (->) (,) (K1 i c)
deriving via (FromContra (Compose f g)) instance (Functor f, Contravariant g) => Bimodule (->) (,) (Compose f g)
deriving via (FromContra (f :.: g))     instance (Functor f, Contravariant g) => Bimodule (->) (,) (f :.: g)
deriving via (FromContra (M1 i c f))    instance Contravariant f => Bimodule (->) (,) (M1 i c f)

deriving via (FromContra Comparison)    instance Bimodule Op Either Comparison
deriving via (FromContra Equivalence)   instance Bimodule Op Either Equivalence
deriving via (FromContra Predicate)     instance Bimodule Op Either Predicate
deriving via (FromContra (Op a))        instance Bimodule Op Either (Op a)
deriving via (FromContra Proxy)         instance Bimodule Op Either Proxy
deriving via (FromContra U1)            instance Bimodule Op Either U1
deriving via (FromContra V1)            instance Bimodule Op Either V1
deriving via (FromContra (Const a))     instance Bimodule Op Either (Const a)
deriving via (FromContra (Alt f))       instance Contravariant f => Bimodule Op Either (Alt f)
deriving via (FromContra (Rec1 f))      instance Contravariant f => Bimodule Op Either (Rec1 f)
deriving via (FromContra (Product f g)) instance (Contravariant f, Contravariant g) => Bimodule Op Either (Product f g)
deriving via (FromContra (Sum f g))     instance (Contravariant f, Contravariant g) => Bimodule Op Either (Sum f g)
deriving via (FromContra (f :+: g))     instance (Contravariant f, Contravariant g) => Bimodule Op Either (f :+: g)
deriving via (FromContra (f :*: g))     instance (Contravariant f, Contravariant g) => Bimodule Op Either (f :*: g)
deriving via (FromContra (K1 i c))      instance Bimodule Op Either (K1 i c)
deriving via (FromContra (Compose f g)) instance (Functor f, Contravariant g) => Bimodule Op Either (Compose f g)
deriving via (FromContra (f :.: g))     instance (Functor f, Contravariant g) => Bimodule Op Either (f :.: g)
deriving via (FromContra (M1 i c f))    instance Contravariant f => Bimodule Op Either (M1 i c f)

deriving via FromFunctor Identity           instance Bimodule (->) Either Identity
deriving via FromFunctor (Compose f g)      instance (Functor f, Functor g) => Bimodule (->) Either (Compose f g)
deriving via FromFunctor []                 instance Bimodule (->) Either []
deriving via FromFunctor ZipList            instance Bimodule (->) Either ZipList
deriving via FromFunctor NonEmpty           instance Bimodule (->) Either NonEmpty
deriving via FromFunctor Maybe              instance Bimodule (->) Either Maybe
deriving via FromFunctor (Either e)         instance Bimodule (->) Either (Either e)
deriving via FromFunctor (These a)          instance Bimodule (->) Either (These a)
deriving via FromFunctor IO                 instance Bimodule (->) Either IO
deriving via FromFunctor (Sum f g)          instance (Functor f, Functor g) => Bimodule (->) Either (Sum f g)
deriving via FromFunctor (Product f g)      instance (Functor f, Functor g) => Bimodule (->) Either (Product f g)
deriving via (FromFunctor ((,) x1))         instance Bimodule (->) Either ((,) x1)
deriving via (FromFunctor ((,,) x1 x2))     instance Bimodule (->) Either ((,,) x1 x2)
deriving via (FromFunctor ((,,,) x1 x2 x3)) instance Bimodule (->) Either ((,,,) x1 x2 x3)

deriving via FromFunctor Identity           instance Bimodule (->) These Identity
deriving via FromFunctor (Compose f g)      instance (Functor f, Functor g) => Bimodule (->) These (Compose f g)
deriving via FromFunctor []                 instance Bimodule (->) These []
deriving via FromFunctor ZipList            instance Bimodule (->) These ZipList
deriving via FromFunctor NonEmpty           instance Bimodule (->) These NonEmpty
deriving via FromFunctor Maybe              instance Bimodule (->) These Maybe
deriving via FromFunctor (Either e)         instance Bimodule (->) These (Either e)
deriving via FromFunctor (These a)          instance Bimodule (->) These (These a)
deriving via FromFunctor IO                 instance Bimodule (->) These IO
deriving via FromFunctor (Sum f g)          instance (Functor f, Functor g) => Bimodule (->) These (Sum f g)
deriving via FromFunctor (Product f g)      instance (Functor f, Functor g) => Bimodule (->) These (Product f g)
deriving via (FromFunctor ((,) x1))         instance Bimodule (->) These ((,) x1)
deriving via (FromFunctor ((,,) x1 x2))     instance Bimodule (->) These ((,,) x1 x2)
deriving via (FromFunctor ((,,,) x1 x2 x3)) instance Bimodule (->) These ((,,,) x1 x2 x3)

deriving via FromFunctor Identity           instance Bimodule Op (,) Identity
deriving via FromFunctor (Compose f g)      instance (Functor f, Functor g) => Bimodule Op (,) (Compose f g)
deriving via FromFunctor []                 instance Bimodule Op (,) []
deriving via FromFunctor ZipList            instance Bimodule Op (,) ZipList
deriving via FromFunctor NonEmpty           instance Bimodule Op (,) NonEmpty
deriving via FromFunctor Maybe              instance Bimodule Op (,) Maybe
deriving via FromFunctor (Either e)         instance Bimodule Op (,) (Either e)
deriving via FromFunctor IO                 instance Bimodule Op (,) IO
deriving via FromFunctor (Sum f g)          instance (Functor f, Functor g) => Bimodule Op (,) (Sum f g)
deriving via FromFunctor (Product f g)      instance (Functor f, Functor g) => Bimodule Op (,) (Product f g)
deriving via (FromFunctor ((,) x1))         instance Bimodule Op (,) ((,) x1)
deriving via (FromFunctor ((,,) x1 x2))     instance Bimodule Op (,) ((,,) x1 x2)
deriving via (FromFunctor ((,,,) x1 x2 x3)) instance Bimodule Op (,) ((,,,) x1 x2 x3)
