module Haskell.Data.Bifunctor where

open import Haskell.Prelude

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

record Bifunctor (p : Type → Type → Type) : Type₁ where
  field
    bimap : ∀ {a b c d} → (a → b) → (c → d) → p a c → p b d
    first : ∀ {a b c} → (a → b) → p a c → p b c
    second : ∀ {a b c} → (b → c) → p a b → p a c

record BifunctorFromBimap (p : Type → Type → Type) : Type₁ where
  field
    bimap : ∀ {a b c d} → (a → b) → (c → d) → p a c → p b d

  first : ∀ {a b c} → (a → b) → p a c → p b c
  first f = bimap f id

  second : ∀ {a b c} → (b → c) → p a b → p a c
  second = bimap id

record BifunctorFromFirstSecond (p : Type → Type → Type) : Type₁ where
  field
    first : ∀ {a b c} → (a → b) → p a c → p b c
    second : ∀ {a b c} → (b → c) → p a b → p a c

  bimap : ∀ {a b c d} → (a → b) → (c → d) → p a c → p b d
  bimap f g = first f ∘ second g

open Bifunctor ⦃ ... ⦄ public

{-# COMPILE AGDA2HS Bifunctor existing-class #-}

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

instance

  iBifunctorFromBimapTuple : BifunctorFromBimap _×_
  iBifunctorFromBimapTuple .BifunctorFromBimap.bimap f g p = f (p .fst) , g (p .snd)

  iBifunctorTuple : Bifunctor _×_
  iBifunctorTuple = record{BifunctorFromBimap iBifunctorFromBimapTuple}

  iBifunctorFromBimapEither : BifunctorFromBimap Either
  iBifunctorFromBimapEither .BifunctorFromBimap.bimap f _ (Left x) = Left (f x)
  iBifunctorFromBimapEither .BifunctorFromBimap.bimap _ g (Right x) = Right (g x)

  iBifunctorEither : Bifunctor Either
  iBifunctorEither = record{BifunctorFromBimap iBifunctorFromBimapEither}