Skip to content

Latest commit

 

History

History
246 lines (184 loc) · 9.7 KB

Bifunctors.MD

File metadata and controls

246 lines (184 loc) · 9.7 KB
classDiagram
   Bifoldable~P[+_,+_]~ <|-- Bitraverse~P[+_,+_]~
   Bifunctor~P[+_,+_]~ <|-- Bitraverse
   Bifunctor <|-- Biapply~P[+_,+_]~
   Biapply <|-- Biapplicative~P[+_,+_]~
   Functor~F[+_]~ <|-- Bifunctor
   Functor <|-- Bifunctor
   Functor <|-- Profunctor~P[-_,+_]~
   Bifunctor <|-- Zivariant~Z[-_,+_,+_]~
   Profunctor <|-- Zivariant

  class Functor {
    ) map(F[A], A => B): F[B]
  }
  class Profunctor {
    ) dimap(AA => A, B => BB): P[A,B] => P[AA,BB]
  }
  class Bifunctor {
    ) bimap(A => AA, B => BB): P[A,B] => P[AA,BB]
  }
  class Bifoldable {
    ) bifoldLeft(F[A,B], C, (C,A) => C, (C,B) => C): C
  }
  class Bitraverse {
    ) bitraverse[G: Applicative](F[A,B], A=>G[C], B => G[D]): G[F[C,D]]
  }
  class Biapply {
    ) biApply(F[A,B], F[A=>AA,B=>BB]): F[AA,BB]
  }
  class Biapplicative {
    ) bipure(a: A, b: B): F[A,B]
  }
  class Zivariant {
    ) zimap(AA => A, B => BB, C => CC): P[A,B,C] => P[AA,BB,CC]
  }
Loading

Bifunctor

Abstracts over type constructor with 2 "holes". Represents two independent functors:

trait Bifunctor[F[_, _]] {
  def bimap[A, B, C, D](fab: F[A, B])(f: A => C, g: B => D): F[C, D]
}
  • Bifunctor Laws
  1. identity xs.bimap(identity, identity) == xs bimap with two identify function does nothing
flowchart LR
  PAB1(("P[A,B]")) --"bimap(id,id)"--> PAB2(("P[A,B]"))
Loading
  1. composition xs.bimap(f, h).bimap(g,i) == xs.bimap(x => g(f(x), x => h(i(x)) you can bimap using f and h and then bimap using g and i or bimap once using composition

Second law is automatically fulfilled if the first law holds.

flowchart LR
  PAB(("P[A,B]")) --"bimap(f1 andThen f2, g1 andTHen g2)"--> PAABB(("P[A2,B2]"))
  PAB(("P[A,B]")) --"bimap(f1,g1)"--> PA1B1(("P[A1,B1]"))--"bimap(f2,g2)"--> PAABB(("P[A2,B2]"))
Loading
  • Alternatively can be specified by providing
def leftMap[A, B, C](fab: F[A, B])(f: A => C): F[C, B]
def rightMap[A, B, D](fab: F[A, B])(g: B => D): F[A, D]

In that case identity law must hold for both functions:
3. identity xs.leftMap(identity) == xs leftMap with identify function does nothing

flowchart LR
  PABL1(("P[A,B]")) --"leftMap(id)"--> PABL2(("P[A,B]"))
Loading
  1. identity xs.rightMap(identity) == xs rightMap with identify function does nothing
flowchart LR
  PABR1(("P[A,B]")) --"rihtMap(id)"--> PABR2(("P[A,B]"))
Loading

If leftMap and rightMap and bimap are specified then additional law must be fullfilled:
5. xs.bimap(f, g) == xs.leftMap(f).rightMap(g)

flowchart LR
  PAB(("P[A,B]")) --"bimap(f, g)"--> PAABB(("P[A2,B2]"))
  PAB(("P[A,B]")) --"leftMap(f)"--> PA1B1(("P[A1,B]"))--"rightMap(g)"--> PAABB(("P[A2,B2]"))
Loading
  • Derived methods
def leftMap[A, B, C](fab: F[A, B])(f: A => C): F[C, B]
def rightMap[A, B, D](fab: F[A, B])(g: B => D): F[A, D]
def leftFunctor[X]: Functor[F[?, X]]
def rightFunctor[X]: Functor[F[X, ?]]
def umap[A, B](faa: F[A, A])(f: A => B): F[B, B]
def widen[A, B, C >: A, D >: B](fab: F[A, B]): F[C, D]

Bifunctor Join

Turn a Bifunctor with both arguments with the same type into Functor.

newtype Join p a = Join { runJoin :: p a a }

-- instance
-- Bifunctor p => Functor (Join p)

Bifunctor Wrap

Functor over second argument of a Bifunctor

newtype WrappedBifunctor p a b = WrapBifunctor { unwrapBifunctor :: p a b }

-- instance
-- Bifunctor p => Functor (WrappedBifunctor p a)

Bifunctor Flip

Swap arguments of Bifunctor

newtype Flip p a b = Flip { runFlip :: p b a }

-- instance
-- Bifunctor p => Bifunctor (Flip p)

Bifunctor Joker

Functor over second argument of Bifunctor

newtype Joker g a b = Joker { runJoker :: g b }

-- instance
-- Functor g => Bifunctor (Joker g :: * -> * -> *)
-- Functor g => Functor (Joker g a)

Bifunctor Clown

Functor over second argument of Bifunctor

newtype Clown f a b = Clown { runClown :: f a }

-- instances
-- Functor (Clown f a :: * -> *)
-- Functor f => Bifunctor (Clown f :: * -> * -> *)

Bifunctor Product

Product of two Bifunctors

data Product f g a b = Pair (f a b) (g a b)

-- instance
-- (Bifunctor f, Bifunctor g) => Bifunctor (Product f g)

Bifunctor Sum

Sum of two Bifunctors

data Sum p q a b = L2 (p a b) | R2 (q a b)

-- instance
-- (Functor f, Bifunctor p) => Functor (Tannen f p a)

Bifunctor Tannen

Compose Functor on the outside.

newtype Tannen f p a b = Tannen { runTannen :: f (p a b) }

-- instances
-- (Functor f, Bifunctor p) => Bifunctor (Tannen f p)
-- (Functor f, Bifunctor p) => Functor (Tannen f p a)

Bifunctor Biff

Compose two Functors inside Bifunctor

newtype Biff p f g a b = Biff { runBiff :: p (f a) (g b) }

-- instance
-- (Bifunctor p, Functor f, Functor g) => Bifunctor (Biff p f g)

Bitraverse

Bifoldable

Biapplicative