{-# LANGUAGE Safe #-}
{-# LANGUAGE NoImplicitPrelude #-}

module Protolude.Bifunctor
  ( Bifunctor,
    bimap,
    first,
    second,
  )
where

import Control.Applicative (Const (Const))
import Data.Either (Either (Left, Right))
import Data.Function ((.), id)

class Bifunctor p where
  {-# MINIMAL bimap | first, second #-}

  bimap :: (a -> b) -> (c -> d) -> p a c -> p b d
  bimap a -> b
f c -> d
g = (a -> b) -> p a d -> p b d
forall (p :: * -> * -> *) a b c.
Bifunctor p =>
(a -> b) -> p a c -> p b c
first a -> b
f (p a d -> p b d) -> (p a c -> p a d) -> p a c -> p b d
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (c -> d) -> p a c -> p a d
forall (p :: * -> * -> *) b c a.
Bifunctor p =>
(b -> c) -> p a b -> p a c
second c -> d
g

  first :: (a -> b) -> p a c -> p b c
  first a -> b
f = (a -> b) -> (c -> c) -> p a c -> p b c
forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap a -> b
f c -> c
forall a. a -> a
id

  second :: (b -> c) -> p a b -> p a c
  second = (a -> a) -> (b -> c) -> p a b -> p a c
forall (p :: * -> * -> *) a b c d.
Bifunctor p =>
(a -> b) -> (c -> d) -> p a c -> p b d
bimap a -> a
forall a. a -> a
id

instance Bifunctor (,) where
  bimap :: (a -> b) -> (c -> d) -> (a, c) -> (b, d)
bimap a -> b
f c -> d
g ~(a
a, c
b) = (a -> b
f a
a, c -> d
g c
b)

instance Bifunctor ((,,) x1) where
  bimap :: (a -> b) -> (c -> d) -> (x1, a, c) -> (x1, b, d)
bimap a -> b
f c -> d
g ~(x1
x1, a
a, c
b) = (x1
x1, a -> b
f a
a, c -> d
g c
b)

instance Bifunctor ((,,,) x1 x2) where
  bimap :: (a -> b) -> (c -> d) -> (x1, x2, a, c) -> (x1, x2, b, d)
bimap a -> b
f c -> d
g ~(x1
x1, x2
x2, a
a, c
b) = (x1
x1, x2
x2, a -> b
f a
a, c -> d
g c
b)

instance Bifunctor ((,,,,) x1 x2 x3) where
  bimap :: (a -> b) -> (c -> d) -> (x1, x2, x3, a, c) -> (x1, x2, x3, b, d)
bimap a -> b
f c -> d
g ~(x1
x1, x2
x2, x3
x3, a
a, c
b) = (x1
x1, x2
x2, x3
x3, a -> b
f a
a, c -> d
g c
b)

instance Bifunctor ((,,,,,) x1 x2 x3 x4) where
  bimap :: (a -> b)
-> (c -> d) -> (x1, x2, x3, x4, a, c) -> (x1, x2, x3, x4, b, d)
bimap a -> b
f c -> d
g ~(x1
x1, x2
x2, x3
x3, x4
x4, a
a, c
b) = (x1
x1, x2
x2, x3
x3, x4
x4, a -> b
f a
a, c -> d
g c
b)

instance Bifunctor ((,,,,,,) x1 x2 x3 x4 x5) where
  bimap :: (a -> b)
-> (c -> d)
-> (x1, x2, x3, x4, x5, a, c)
-> (x1, x2, x3, x4, x5, b, d)
bimap a -> b
f c -> d
g ~(x1
x1, x2
x2, x3
x3, x4
x4, x5
x5, a
a, c
b) = (x1
x1, x2
x2, x3
x3, x4
x4, x5
x5, a -> b
f a
a, c -> d
g c
b)

instance Bifunctor Either where
  bimap :: (a -> b) -> (c -> d) -> Either a c -> Either b d
bimap a -> b
f c -> d
_ (Left a
a) = b -> Either b d
forall a b. a -> Either a b
Left (a -> b
f a
a)
  bimap a -> b
_ c -> d
g (Right c
b) = d -> Either b d
forall a b. b -> Either a b
Right (c -> d
g c
b)

instance Bifunctor Const where
  bimap :: (a -> b) -> (c -> d) -> Const a c -> Const b d
bimap a -> b
f c -> d
_ (Const a
a) = b -> Const b d
forall k a (b :: k). a -> Const a b
Const (a -> b
f a
a)