All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scalaz.Cokleisli.scala Maven / Gradle / Ivy

package scalaz

final case class Cokleisli[F[_], A, B](run: F[A] => B) { self =>
  def apply(fa: F[A]): B =
    run(fa)


  def dimap[C, D](f: C => A, g: B => D)(implicit b: Functor[F]): Cokleisli[F, C, D] =
    Cokleisli(c => g(run(b.map(c)(f)))) // b.map(run(f(c)))(g))

  def contramapValue[C](f: F[C] => F[A]): Cokleisli[F, C,  B] = Cokleisli(run compose f)

  def map[C](f: B => C): Cokleisli[F, A, C] = Cokleisli(f compose run)

  def flatMap[C](f: B => Cokleisli[F, A, C]): Cokleisli[F, A, C] =
    Cokleisli(fa => f(self.run(fa)).run(fa))

  def <<=(a: F[A])(implicit F: Cobind[F]): F[B] =
    F.extend(a)(run)

  def =>=[C](c: Cokleisli[F, B, C])(implicit F: Cobind[F]): Cokleisli[F, A, C] =
    Cokleisli(fa => c run (<<=(fa)))

  def compose[C](c: Cokleisli[F, C, A])(implicit F: Cobind[F]): Cokleisli[F, C, B] =
    c =>= this

  def =<=[C](c: Cokleisli[F, C, A])(implicit F: Cobind[F]): Cokleisli[F, C, B] =
    compose(c)

  def endo(implicit ev: B === A): Endomorphic[Cokleisli[F, *, *], A] =
    Endomorphic[Cokleisli[F, *, *], A](ev.subst[Cokleisli[F, A, *]](this))
}

object Cokleisli extends CokleisliInstances {

}

sealed abstract class CokleisliInstances0 {
  implicit def cokleisliCompose[F[_]](implicit F0: Cobind[F]): Compose[Cokleisli[F, *, *]] =
    new CokleisliCompose[F] {
      override def F = F0
    }
  implicit def cokleisliProfunctor[F[_]: Functor]: Profunctor[Cokleisli[F, *, *]] =
    new CokleisliProfunctor[F] {
      def F = implicitly
    }
}

sealed abstract class CokleisliInstances extends CokleisliInstances0 {
  implicit def cokleisliMonad[F[_], R]: Monad[Cokleisli[F, R, *]] & BindRec[Cokleisli[F, R, *]] =
    new CokleisliMonad[F, R] {}

  implicit def cokleisliArrow[F[_]](implicit F0: Comonad[F]): Arrow[Cokleisli[F, *, *]] & ProChoice[Cokleisli[F, *, *]] =
    new CokleisliArrow[F] {
      override def F = F0
    }
}

private trait CokleisliMonad[F[_], R] extends Monad[Cokleisli[F, R, *]] with BindRec[Cokleisli[F, R, *]] {
  override def map[A, B](fa: Cokleisli[F, R, A])(f: A => B) = fa map f
  override def ap[A, B](fa: => Cokleisli[F, R, A])(f: => Cokleisli[F, R, A => B]) = f flatMap (fa map _)
  def point[A](a: => A) = Cokleisli(_ => a)
  def bind[A, B](fa: Cokleisli[F, R, A])(f: A => Cokleisli[F, R, B]) = fa flatMap f
  def tailrecM[A, B](a: A)(f: A => Cokleisli[F, R, A \/ B]): Cokleisli[F, R, B] = {
    @annotation.tailrec
    def go(a0: A)(r: F[R]): B =
      f(a0).run(r) match {
        case -\/(a1) => go(a1)(r)
        case \/-(b) => b
      }

    Cokleisli(go(a))
  }
}

private trait CokleisliCompose[F[_]] extends Compose[Cokleisli[F, *, *]] {
  implicit def F: Cobind[F]

  override def compose[A, B, C](f: Cokleisli[F, B, C], g: Cokleisli[F, A, B]) = f compose g
}

private trait CokleisliProfunctor[F[_]] extends Profunctor[Cokleisli[F, *, *]] {
  implicit def F: Functor[F]

  override def dimap[A, B, C, D](fab: Cokleisli[F, A, B])(f: C => A)(g: B => D) =
    fab.dimap(f, g)

  override final def mapfst[A, B, C](fa: Cokleisli[F, A, B])(f: C => A) =
    Cokleisli[F, C, B](fc => fa(F.map(fc)(f)))

  override final def mapsnd[A, B, C](fa: Cokleisli[F, A, B])(f: B => C) =
    fa map f
}

private trait CokleisliArrow[F[_]]
  extends Arrow[Cokleisli[F, *, *]]
  with ProChoice[Cokleisli[F, *, *]]
  with CokleisliProfunctor[F]
  with CokleisliCompose[F] {

  implicit def F: Comonad[F]

  def left[A, B, C](fa: Cokleisli[F, A, B]): Cokleisli[F, A \/ C, B \/ C] =
    Cokleisli { (ac: F[A \/ C]) =>
      F.copoint(ac) match {
        case -\/(a) => -\/(fa run (F.map(ac)(_ => a)))
        case x @ \/-(_) => x.coerceLeft
      }
    }

  def right[A, B, C](fa: Cokleisli[F, A, B]): Cokleisli[F, C \/ A, C \/ B] =
    Cokleisli { (ac: F[C \/ A]) =>
      F.copoint(ac) match {
        case x @ -\/(_) => x.coerceRight
        case \/-(a) => \/-(fa run (F.map(ac)(_ => a)))
      }
    }

  def arr[A, B](f: A => B) = Cokleisli(a => f(F.copoint(a)))
  def id[A] = Cokleisli[F, A, A](F.copoint)

  def first[A, B, C](f: Cokleisli[F, A, B]) =
      Cokleisli[F, (A, C), (B, C)](w => (f.run(F.map(w)(_._1)), F.copoint(w)._2))
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy