module Haskell.Data.Bifunctor where

open import Haskell.Prim using (Type; id; _∘_)
open import Haskell.Prim.Functor using (Functor)
open import Haskell.Prim.Tuple using (_×_; _,_; fst; snd)
open import Haskell.Prim.Either using (Either; Left; Right)

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

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}