package com.github.everpeace
import scalaz._
import Scalaz._
trait BiKleisli[W[_], M[_], A, B] {
def apply(wa: W[A]): M[B]
import BiKleislis.{bikleisli, bikleisliFn}
def ##[C](g: BiKleisli[W, M, B, C])(implicit w: Comonad[W], m: Monad[M], t: Distributes[W, M]): BiKleisli[W, M, A, C] = bikleisli((wa: W[A]) => t(wa =>> this) >>= g)
}
object BiKleislis {
def bikleisli[W[_], M[_], A, B](f: W[A] => M[B])(implicit w: Comonad[W], m: Monad[M], t: Distributes[W, M]): BiKleisli[W, M, A, B] = new BiKleisli[W, M, A, B] {
def apply(wa: W[A]) = f(wa)
}
def ★☆[W[_], M[_], A, B](f: W[A] => M[B])(implicit w: Comonad[W], m: Monad[M], t: Distributes[W, M]): BiKleisli[W, M, A, B] = bikleisli(f)
implicit def bikleisliFn[W[_], M[_], A, B](bk: BiKleisli[W, M, A, B]): W[A] => M[B] = bk(_)
implicit def bikleislimab[W[_], M[_], A, B](bk: BiKleisli[W, M, A, B]) = mab[({type λ[α, β] = BiKleisli[W, M, α, β]})#λ, A, B](bk)
implicit def BiKleisliCategory[W[_], M[_]](implicit w: Comonad[W], m: Monad[M], t: Distributes[W, M]): Category[({type λ[α, β] = BiKleisli[W, M, α, β]})#λ]
= new Category[({type λ[α, β] = BiKleisli[W, M, α, β]})#λ] {
def id[A] = bikleisli((wa: W[A]) => m.pure(w.copure(wa)))
def compose[A, B, C](f: BiKleisli[W, M, B, C], g: BiKleisli[W, M, A, B]) = g ## f
}
implicit def BiKleisliArrow[W[_],M[_]](implicit w: Comonad[W], m: Monad[M], t: Distributes[W, M]):Arrow[({type λ[α, β] = BiKleisli[W, M, α, β]})#λ]
= new Arrow[({type λ[α, β] = BiKleisli[W, M, α, β]})#λ] {
def arrow[B, C](f: B => C) = ★☆( (wb:W[B]) => f(wb copure) pure )
def first[B, C, D](a: ({type λ[α, β] = BiKleisli[W, M, α, β]})#λ[B, C]) = ★☆((x:W[(B,D)]) => a(wfst(x)) >>= ( (c:C)=> (c , wsnd(x) copure) pure ) )
def second[B, C, D](a: ({type λ[α, β] = BiKleisli[W, M, α, β]})#λ[B, C]) = ★☆((x:W[(D,B)]) => a(wsnd(x)) >>= ( (c:C)=> (wfst(x) copure, c ) pure ) )
def wfst[X,Y](t:W[(X,Y)]):W[X]= w.fmap(t,(x:(X,Y))=>x._1)
def wsnd[X,Y](t:W[(X,Y)]):W[Y]= w.fmap(t,(x:(X,Y))=>x._2)
val category = BiKleisliCategory
}
}