{-# LANGUAGE LinearTypes #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# OPTIONS_HADDOCK hide #-}

module Data.Bifunctor.Linear.Internal.Bifunctor
  ( Bifunctor (..),
  )
where

import Prelude.Linear

-- | The Bifunctor class
--
-- == Laws
--
-- If 'bimap' is supplied, then
-- @'bimap' 'id' 'id' = 'id'@
--
-- * If 'first' and 'second' are supplied, then
-- @
-- 'first' 'id' ≡ 'id'
-- 'second' 'id' ≡ 'id'
-- @
--
-- * If all are supplied, then
-- @'bimap' f g = 'first' f '.' 'second' g
class Bifunctor p where
  {-# MINIMAL bimap | (first, second) #-}
  bimap :: (a %1 -> b) -> (c %1 -> d) -> a `p` c %1 -> b `p` d
  bimap f g x = first f (second g x)
  {-# INLINE bimap #-}

  first :: (a %1 -> b) -> a `p` c %1 -> b `p` c
  first f = bimap f id
  {-# INLINE first #-}

  second :: (b %1 -> c) -> a `p` b %1 -> a `p` c
  second = bimap id
  {-# INLINE second #-}

-- # Instances
-------------------------------------------------------------------------------

instance Bifunctor (,) where
  bimap f g (x, y) = (f x, g y)
  first f (x, y) = (f x, y)
  second g (x, y) = (x, g y)

instance Bifunctor Either where
  bimap f g = either (Left . f) (Right . g)
