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

scalaz.Day.scala Maven / Gradle / Ivy

package scalaz

/**
  * Covariant Day Convolution
  *
  * Based on Edward Kmett implementation in Haskell: [[https://hackage.haskell.org/package/kan-extensions/docs/Data-Functor-Day.html]]
  *
  * Day convolution is a special form of Functor multiplication.
  * In monoidal category of endofunctors Applicative is a monoid object when Day covolution is used as tensor.
  * If we use Functor composition as tensor then then monoid form a Monad instead of Applicative.
  *
  * Can be seen as generalization of method apply2 from Apply:
  * {{{
  * def apply2(fa => F[A], fb => F[B])(f: (A, B) => C): F[C]
  *
  * trait Day[F[_], G[_], A] { self =>
  *   // ...
  *   val fx: F[X]
  *   val gy: G[Y]
  *   def xya: (X, Y) => A
  * }
  * }}}
  *
  * @see [[https://www.youtube.com/watch?v=lIWCxRBaQG8 Bartosz Milewski talk, derive Day from multiplying Functors and using Coyoneda]]
  * @see [[https://www.youtube.com/watch?v=cB8DapKQz-I Edward Kmett talk, explains Divisible and Decidable in context of contravariant Day]]
  * @see [[https://blog.functorial.com/posts/2016-08-08-Comonad-And-Day-Convolution.html Phil Freeman blog, connections between Day and Comonads, Comonads transformers, optics]]
  * @see [[https://www.reddit.com/r/haskell/comments/4wvae2/functorial_blog_comonads_and_day_convolution/ Discussion on Reddit with mention about usage for stream processing and intuition based on liftA2 from Applicative and Day]]
  */
trait Day[F[_], G[_], A] { self =>
  type X
  type Y
  val fx: F[X]
  val gy: G[Y]
  def xya: (X, Y) => A

  def map[B](f: A => B): Day[F, G, B] = Day[F, G, B, X, Y](fx, gy, (x, y) => f(self.xya(x, y)))

  def cobind[B](f: Day[F, G, A] => B): Day[F, G, B] = Day[F, G, B, X, Y](fx, gy, (_, _) => f(self))

  /** Swap type constructors order */
  def swapped: Day[G, F, A] = Day[G, F, A, Y, X](gy, fx, (x, y) => self.xya(y, x))

  /** Apply a natural transformation to the left-hand side of a Day convolution. */
  def trans1[H[_]](nat: F ~> H): Day[H, G, A] = Day(nat.apply(fx), gy, xya)

  /** Apply a natural transformation to the right-hand side of a Day convolution. */
  def trans2[H[_]](nat: G ~> H): Day[F, H, A] = Day(fx, nat.apply(gy), xya)
}

object Day extends DayInstances {
  import scalaz.Id.Id

  def apply[F[_], G[_], A, XX, YY](fa: F[XX], gb: G[YY], abc: (XX, YY) => A): Day[F, G, A] =
    new Day[F, G, A] {
      type X = XX
      type Y = YY
      val fx: F[X] = fa
      val gy: G[Y] = gb
      def xya: (X, Y) => A = abc
    }

  /** Construct the Day convolution */
  def day[F[_], G[_], A, B](fab: F[A => B], ga: G[A]): Day[F, G, B] = Day[F, G, B, A => B, A](fab, ga, (x, y) => x(y))

  def intro1[F[_], A](fa: F[A]): Day[Id, F, A] = Day[Id, F, A, Unit, A]((), fa, (_, a) => a)

  def intro2[F[_], A](fa: F[A]): Day[F, Id, A] = Day[F, Id, A, A, Unit](fa, (), (a, _) => a)

  /** Collapse to second type constructor if first one is Identity */
  def elim1[F[_], A](d: Day[Id, F, A])(implicit FF: Functor[F]): F[A] = FF.map(d.gy)(d.xya(d.fx, _))

  /** Collapse to first type constructor if second one is Identity */
  def elim2[F[_], A](d: Day[F, Id, A])(implicit FF: Functor[F]): F[A] = FF.map(d.fx)(d.xya(_, d.gy))

  /** Collapse to type constructor if both of them are the same */
  def dap[F[_], A](d: Day[F, F, A])(implicit AF: Applicative[F]): F[A] = AF.apply2(d.fx, d.gy)(d.xya)

  def assoc[F[_], G[_], H[_], A, B](d: Day[F, Day[G, H, *], A]): Day[Day[F, G, *], H, A] = {
    val fx = Day[F, G, (d.X, d.gy.X), d.X, d.gy.X](d.fx, d.gy.fx, (x, y) => (x, y))
    Day[Day[F, G, *], H, A, (d.X, d.gy.X), d.gy.Y](fx, d.gy.gy, (a, e) => d.xya(a._1, d.gy.xya(a._2, e)))
  }

  def disassoc[F[_], G[_], H[_], A](d: Day[Day[F, G, *], H, A]): Day[F, Day[G, H, *], A] = {
    val gyy:  Day[G, H, (d.fx.Y, d.Y)] = Day[G, H, (d.fx.Y, d.Y), d.fx.Y, d.Y](d.fx.gy, d.gy, (x,y) => (x,y))
    Day[F, Day[G, H, *], A, d.fx.X, (d.fx.Y, d.Y)](d.fx.fx, gyy, (x: d.fx.X, y: (d.fx.Y, d.Y)) => d.xya(d.fx.xya(x, y._1), y._2))
  }
}

sealed abstract class DayInstances extends DayInstances1 {

  implicit def cohoistDay[F[_]](implicit F: Comonad[F]): Cohoist[({type l[X[_], Y] = Day[F, X, Y]})#l] =
    new Cohoist[({type l[X[_], Y] = Day[F, X, Y]})#l] {
      override def lower[G[_], A](a: Day[F, G, A])(implicit G: Cobind[G]): G[A] =
        G.map(a.gy)(a.xya(F.copoint(a.fx), _))

      override def cohoist[M[_], N[_]: Comonad](f: M ~> N): Day[F, M, *] ~> Day[F, N, *] =
        new (Day[F, M, *] ~> Day[F, N, *]){
          def apply[A](a: Day[F, M, A]) = a trans2 f
        }
    }

  implicit def comonadDay[F[_], G[_]](implicit CF0: Comonad[F], CG0: Comonad[G]): Comonad[Day[F, G, *]] = new DayComonad[F, G] {
    def CF: Comonad[F] = CF0
    def CG: Comonad[G] = CG0
  }
}

sealed abstract class DayInstances1 extends DayInstances2 {

  implicit def cobindDay[F[_], G[_]]: Cobind[Day[F, G, *]] = new DayCobind[F, G] {}
}

sealed abstract class DayInstances2 extends DayInstances3 {

  implicit def applicativeDay[F[_], G[_]](implicit AF0: Applicative[F], AG0: Applicative[G]): Applicative[Day[F, G, *]] = new DayApplicative[F, G] {
    def AF: Applicative[F] = AF0
    def AG: Applicative[G] = AG0
  }
}

sealed abstract class DayInstances3 extends DayInstances4 {

  implicit def applyDay[F[_], G[_]](implicit AF0: Apply[F], AG0: Apply[G]): Apply[Day[F, G, *]] = new DayApply[F, G] {
    def AF: Apply[F] = AF0
    def AG: Apply[G] = AG0
  }
}

sealed abstract class DayInstances4 {

  implicit def functorDay[F[_], G[_]]: Functor[Day[F, G, *]] = new DayFunctor[F, G] {}
}

private trait DayFunctor[F[_], G[_]] extends Functor[Day[F, G, *]] {
  override def map[C, D](d: Day[F, G, C])(f: C => D): Day[F, G, D] = d.map(f)
}

private trait DayCobind[F[_], G[_]] extends Cobind[Day[F, G, *]] with DayFunctor[F, G] {
  override def cobind[A, B](fa: Day[F, G, A])(f: Day[F, G, A] => B): Day[F, G, B] = fa.cobind(f)
}

private trait DayComonad[F[_], G[_]] extends Comonad[Day[F, G, *]] with DayCobind[F, G] {
  def CF: Comonad[F]
  def CG: Comonad[G]

  def copoint[C](w: Day[F, G, C]): C = w.xya(CF.copoint(w.fx), CG.copoint(w.gy))
}

private trait DayApply[F[_], G[_]] extends Apply[Day[F, G, *]] with DayFunctor[F, G] {
  def AF: Apply[F]
  def AG: Apply[G]

  def ap[A,B](d: => Day[F, G, A])(f: => Day[F, G, A => B]): Day[F, G, B] = strictAp(d)(f)

  /* Workaround for: stable identifier required, but d found */
  private def strictAp[A,B](d: Day[F, G, A])(f: Day[F, G, A => B]): Day[F, G, B] = {
    val fxx: F[(f.X, d.X)] = AF.ap(d.fx)(AF.map(f.fx)(a => b => (a, b)))
    val gyy: G[(f.Y, d.Y)] = AG.ap(d.gy)(AG.map(f.gy)(a => b => (a, b)))
    val abc: ((f.X, d.X), (f.Y, d.Y)) => B = (a, b) => f.xya(a._1, b._1)(d.xya(a._2, b._2))
    Day(fxx, gyy, abc)
  }
}

private trait DayApplicative[F[_], G[_]] extends Applicative[Day[F, G, *]] with DayApply[F, G] {
  def AF: Applicative[F]
  def AG: Applicative[G]

  override def point[A](a: => A): Day[F, G, A] = Day[F, G, A, Unit, Unit](AF.pure(()), AG.pure(()), (_, _) => a)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy