    {-# LANGUAGE FlexibleContexts #-}
    {-# LANGUAGE MultiParamTypeClasses #-}

    -- |
    -- Module      : Data.Functor.ProductIsomorphic.Class
    -- Copyright   : 2017 Kei Hibino
    -- License     : BSD3
    --
    -- Maintainer  : ex8k.hibino@gmail.com
    -- Stability   : experimental
    -- Portability : unknown
    --
    -- This module defines functor interfaces which morphed functions
    -- are restricted to products.
    module Data.Functor.ProductIsomorphic.Class (
      -- * ProductIso classes
      ProductIsoFunctor (..),
      ProductIsoApplicative (..),
      ProductIsoAlternative (..),

      -- * Empty element
      ProductIsoEmpty (..),
      peRightR, peLeftR,

      --- (<|), (|>),
      ) where

    import Data.Functor.ProductIsomorphic.Unsafe (ProductConstructor)
    import Data.Functor.ProductIsomorphic.TupleInstances ()

    -- | Restricted functor on products.
    class ProductIsoFunctor f where
      (|$|) :: ProductConstructor (a -> b) => (a -> b) -> f a -> f b

    -- | Restricted applicative functor on products.
    class ProductIsoFunctor f => ProductIsoApplicative f where
      pureP :: ProductConstructor a => a -> f a
      (|*|) :: f (a -> b) -> f a -> f b

    -- | Restricted alternative on products.
    class ProductIsoApplicative f => ProductIsoAlternative f where
      emptyP :: f a
      (|||)  :: f a -> f a -> f a

    infixl 4 |$|, |*|
    infixl 3 |||

    -- | Empty element of product operator
    class ProductIsoApplicative f => ProductIsoEmpty f e where
      pureE   :: f e
      peRight :: f (a, e) -> f a
      peLeft  :: f (e, a) -> f a

    -- | peRight and peRightR should have isomorphic law.
    -- @
    --   peRight . peRightR == peRightR . peRight == id
    -- @
    peRightR :: ProductIsoEmpty f e
            => f a
            -> f (a, e)
    peRightR p = (,) |$| p |*| pureE
    {-# INLINABLE peRightR #-}

    -- | peLeft and peLeftR should have isomorphic law.
    -- @
    --   peLeft . peLeftR == peLeftR . peLeft == id
    -- @
    peLeftR :: ProductIsoEmpty f e
           => f a
           -> f (e, a)
    peLeftR p = (,) |$| pureE |*| p
    {-# INLINABLE peLeftR #-}

    {-
    (<|) :: ProductIsoEmpty f e => f a -> f e -> f a
    p <| e = peRight $ (,) |$| p |*| e
    {-# INLINABLE (<|) #-}

    (|>) :: ProductIsoEmpty f e => f e -> f a -> f a
    e |> p = peLeft $ (,) |$| e |*| p
    {-# INLINABLE (|>) #-}

    infixl 4 <|, |>
     -}