Skip to content

Commit

Permalink
Rework Traverse derivation
Browse files Browse the repository at this point in the history
* Make it more consistent with other derivations
* Make sure it's stack safe
* Make sure `forall` and `exists` are lazy
* Add more tests
  • Loading branch information
joroKr21 committed Apr 27, 2019
1 parent bb67301 commit 58cc026
Show file tree
Hide file tree
Showing 8 changed files with 218 additions and 281 deletions.
10 changes: 5 additions & 5 deletions core/src/main/scala/cats/derived/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,8 @@ object auto {

object traverse {
implicit def kittensMkTraverse[F[_]](
implicit refute: Refute[Traverse[F]], ev: MkTraverse[F]
): Traverse[F] = ev
implicit refute: Refute[Traverse[F]], F: Lazy[MkTraverse[F]]
): Traverse[F] = F.value
}

//todo: the regular approach doesn't work for the following instances
Expand Down Expand Up @@ -165,8 +165,8 @@ object cached {

object traverse{
implicit def kittensMkTraverse[F[_]](
implicit refute: Refute[Traverse[F]], ev: Cached[MkTraverse[F]])
: Traverse[F] = ev.value
implicit refute: Refute[Traverse[F]], cached: Cached[MkTraverse[F]]
): Traverse[F] = cached.value
}

object show {
Expand Down Expand Up @@ -266,7 +266,7 @@ object semi {

def foldable[F[_]](implicit F: MkFoldable[F]): Foldable[F] = F

def traverse[F[_]](implicit F: MkTraverse[F]): Traverse[F] = F
def traverse[F[_]](implicit F: Lazy[MkTraverse[F]]): Traverse[F] = F.value

def monoid[A](implicit ev: Lazy[MkMonoid[A]]): Monoid[A] = ev.value

Expand Down
192 changes: 79 additions & 113 deletions core/src/main/scala/cats/derived/traverse.scala
Original file line number Diff line number Diff line change
@@ -1,166 +1,132 @@
package cats.derived

import cats.syntax.all._
import cats.{Applicative, Eval, Now, Traverse}
import cats.{Applicative, Eval, Traverse}
import shapeless._

import scala.annotation.implicitNotFound

/**
* Based on the `MkFoldable` implementation.
*/
@implicitNotFound("Could not derive an instance of Traverse[${F}]")
trait MkTraverse[F[_]] extends Traverse[F] {

def safeTraverse[G[_] : Applicative, A, B](fa: F[A])(f: A => Eval[G[B]]): Eval[G[F[B]]]

def safeTraverse[G[_]: Applicative, A, B](fa: F[A])(f: A => Eval[G[B]]): Eval[G[F[B]]]
def safeFoldLeft[A, B](fa: F[A], b: B)(f: (B, A) => Eval[B]): Eval[B]

def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B]

def traverse[G[_] : Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]] =
safeTraverse(fa)(a => Now(f(a))).value
def traverse[G[_]: Applicative, A, B](fa: F[A])(f: A => G[B]): G[F[B]] =
safeTraverse(fa)(a => Eval.later(f(a))).value

def foldLeft[A, B](fa: F[A], b: B)(f: (B, A) => B): B =
safeFoldLeft(fa, b) { (b, a) => Now(f(b, a)) }.value
safeFoldLeft(fa, b)((b, a) => Eval.later(f(b, a))).value

// The default `forall` is not lazy.
override def forall[A](fa: F[A])(p: A => Boolean): Boolean =
foldRight(fa, Eval.True)((a, lb) => lb.map(_ && p(a))).value

// The default `exists` is not lazy.
override def exists[A](fa: F[A])(p: A => Boolean): Boolean =
foldRight(fa, Eval.False)((a, lb) => lb.map(_ || p(a))).value
}

object MkTraverse extends MkTraverseDerivation {
def apply[F[_]](implicit mff: MkTraverse[F]): MkTraverse[F] = mff
def apply[F[_]](implicit F: MkTraverse[F]): MkTraverse[F] = F
}

trait MkTraverseDerivation extends MkTraverse0 {
implicit val mkTraverseId: MkTraverse[shapeless.Id] = new MkTraverse[shapeless.Id] {
def safeTraverse[G[_] : Applicative, A, B](fa: Id[A])(f: A => Eval[G[B]]): Eval[G[Id[B]]] = f(fa)
private[derived] abstract class MkTraverseDerivation extends MkTraverseNested {
implicit val mkTraverseHNil: MkTraverse[Const[HNil]#λ] = mkTraverseConst
implicit val mkTraverseCNil: MkTraverse[Const[CNil]#λ] = mkTraverseConst

def foldRight[A, B](fa: A, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = f(fa, lb)
implicit def mkTraverseConst[T]: MkTraverse[Const[T]#λ] = new MkTraverse[Const[T]#λ] {
def safeTraverse[G[_], A, B](fa: T)(f: A => Eval[G[B]])(implicit G: Applicative[G]) = Eval.now(G.pure(fa))
def foldRight[A, B](fa: T, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]) = lb
def safeFoldLeft[A, B](fa: T, b: B)(f: (B, A) => Eval[B]) = Eval.now(b)
}
}

def safeFoldLeft[A, B](fa: A, b: B)(f: (B, A) => Eval[B]): Eval[B] = Now(f(b, fa).value)
private[derived] abstract class MkTraverseNested extends MkTraverseCons {

}
implicit def mkTraverseNested[F[_]](implicit F: Split1[F, TraverseOrMk, TraverseOrMk]): MkTraverse[F] =
new MkTraverse[F] {

implicit def mkTraverseConst[T]: MkTraverse[Const[T]#λ] = new MkTraverse[Const[T]#λ] {
override def safeTraverse[G[_] : Applicative, A, B](fa: T)(f: A => Eval[G[B]]): Eval[G[T]] =
Now(fa.pure[G])
def safeTraverse[G[_], A, B](fa: F[A])(f: A => Eval[G[B]])(implicit G: Applicative[G]) =
mkSafeTraverse(F.fo)(F.unpack(fa))(mkSafeTraverse(F.fi)(_)(f)).map(G.map(_)(F.pack))

def foldRight[A, B](fa: T, lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = lb
def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]) =
F.fo.unify.foldRight(F.unpack(fa), lb)(F.fi.unify.foldRight(_, _)(f))

def safeFoldLeft[A, B](fa: T, b: B)(f: (B, A) => Eval[B]): Eval[B] = Now(b)
}
def safeFoldLeft[A, B](fa: F[A], b: B)(f: (B, A) => Eval[B]) =
mkSafeFoldLeft(F.fo)(F.unpack(fa), b)((b, fia) => mkSafeFoldLeft(F.fi)(fia, b)(f))
}
}

private[derived] trait MkTraverse0 extends MkTraverse1 {
// Induction step for products
implicit def mkTraverseHcons[F[_]](implicit ihc: IsHCons1[F, TraverseOrMk, MkTraverse]): MkTraverse[F] =
private[derived] abstract class MkTraverseCons extends MkTraverseGeneric {

implicit def mkTraverseHCons[F[_]](implicit F: IsHCons1[F, TraverseOrMk, MkTraverse]): MkTraverse[F] =
new MkTraverse[F] {
override def safeTraverse[G[_] : Applicative, A, B](fa: F[A])(f: A => Eval[G[B]]): Eval[G[F[B]]] = {
for {
ht <- Now(ihc.unpack(fa))
th <- ihc.fh.unify.safeTraverse(ht._1)(f)
tt <- ihc.ft.safeTraverse(ht._2)(f)
} yield (th, tt).mapN(ihc.pack(_, _))

def safeTraverse[G[_], A, B](fa: F[A])(f: A => Eval[G[B]])(implicit G: Applicative[G]) = {
val (fha, fta) = F.unpack(fa)
mkSafeTraverse(F.fh)(fha)(f).flatMap(gfhb => F.ft.safeTraverse(fta)(f).map(G.map2(gfhb, _)(F.pack(_, _))))
}

def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = {
import ihc._
val (hd, tl) = unpack(fa)
for {
t <- ft.foldRight(tl, lb)(f)
h <- fh.unify.foldRight(hd, Now(t))(f)
} yield h
def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]) = {
val (fha, fta) = F.unpack(fa)
F.ft.foldRight(fta, lb)(f).flatMap(t => F.fh.unify.foldRight(fha, Eval.now(t))(f))
}

def safeFoldLeft[A, B](fa: F[A], b: B)(f: (B, A) => Eval[B]): Eval[B] = {
import ihc._
val (hd, tl) = unpack(fa)
for {
h <- fh.unify.safeFoldLeft(hd, b)(f)
t <- ft.safeFoldLeft(tl, h)(f)
} yield t
def safeFoldLeft[A, B](fa: F[A], b: B)(f: (B, A) => Eval[B]) = {
val (fha, fta) = F.unpack(fa)
mkSafeFoldLeft(F.fh)(fha, b)(f).flatMap(F.ft.safeFoldLeft(fta, _)(f))
}
}

// Induction step for coproducts
implicit def mkTraverseCcons[F[_]](implicit icc: IsCCons1[F, TraverseOrMk, MkTraverse]): MkTraverse[F] =
implicit def mkTraverseCCons[F[_]](implicit F: IsCCons1[F, TraverseOrMk, MkTraverse]): MkTraverse[F] =
new MkTraverse[F] {
override def safeTraverse[G[_] : Applicative, A, B](fa: F[A])(f: A => Eval[G[B]]): Eval[G[F[B]]] = {
val gUnpacked: Eval[G[Either[icc.H[B], icc.T[B]]]] =
icc.unpack(fa) match {
case Left(hd) => apEval[G].map(icc.fh.unify.safeTraverse(hd)(f))(Left(_))
case Right(tl) => apEval[G].map(icc.ft.safeTraverse(tl)(f))(Right(_))
}

apEval[G].map(gUnpacked)(icc.pack)
}

def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = {
import icc._
unpack(fa) match {
case Left(hd) => fh.unify.foldRight(hd, lb)(f)
case Right(tl) => ft.foldRight(tl, lb)(f)
def safeTraverse[G[_], A, B](fa: F[A])(f: A => Eval[G[B]])(implicit G: Applicative[G]) =
F.unpack(fa) match {
case Left(fha) => mkSafeTraverse(F.fh)(fha)(f).map(G.map(_)(fhb => F.pack(Left(fhb))))
case Right(fta) => F.ft.safeTraverse(fta)(f).map(G.map(_)(ftb => F.pack(Right(ftb))))
}
}

def safeFoldLeft[A, B](fa: F[A], b: B)(f: (B, A) => Eval[B]): Eval[B] = {
import icc._
unpack(fa) match {
case Left(hd) => fh.unify.safeFoldLeft(hd, b)(f)
case Right(tl) => ft.safeFoldLeft(tl, b)(f)
def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]) =
F.unpack(fa) match {
case Left(fha) => F.fh.unify.foldRight(fha, lb)(f)
case Right(fta) => F.ft.foldRight(fta, lb)(f)
}
}
}

}

private[derived] trait MkTraverse1 extends MkTraverse2 {
implicit def mkTraverseSplit[F[_]](implicit split: Split1[F, TraverseOrMk, TraverseOrMk]): MkTraverse[F] =
new MkTraverse[F] {
override def safeTraverse[G[_] : Applicative, A, B](fa: F[A])(f: A => Eval[G[B]]): Eval[G[F[B]]] =
split.fo.unify.safeTraverse(split.unpack(fa))(split.fi.unify.safeTraverse(_)(f)).map(_.map(split.pack))

def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] = {
import split._
fo.unify.foldRight(unpack(fa), lb) { (fai, lbi) => fi.unify.foldRight(fai, lbi)(f) }
}

def safeFoldLeft[A, B](fa: F[A], b: B)(f: (B, A) => Eval[B]): Eval[B] = {
import split._
fo.unify.safeFoldLeft(unpack(fa), b) { (lbi, fai) => fi.unify.safeFoldLeft(fai, lbi)(f) }
}
def safeFoldLeft[A, B](fa: F[A], b: B)(f: (B, A) => Eval[B]) =
F.unpack(fa) match {
case Left(fha) => mkSafeFoldLeft(F.fh)(fha, b)(f)
case Right(fta) => F.ft.safeFoldLeft(fta, b)(f)
}
}
}

private[derived] trait MkTraverse2 extends MkTraverseUtils {
implicit def mkTraverseGeneric[F[_]](implicit gen: Generic1[F, MkTraverse]): MkTraverse[F] =
new MkTraverse[F] {
override def safeTraverse[G[_] : Applicative, A, B](fa: F[A])(f: A => Eval[G[B]]): Eval[G[F[B]]] =
gen.fr.safeTraverse(gen.to(fa))(f).map(_.map(gen.from))
private[derived] abstract class MkTraverseGeneric {
protected type TraverseOrMk[F[_]] = Traverse[F] OrElse MkTraverse[F]

def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]): Eval[B] =
gen.fr.foldRight(gen.to(fa), lb)(f)
protected def mkSafeTraverse[F[_], G[_]: Applicative, A, B](
F: TraverseOrMk[F]
)(fa: F[A])(f: A => Eval[G[B]]): Eval[G[F[B]]] = F.unify match {
case mk: MkTraverse[F] => mk.safeTraverse(fa)(f)
case other => other.traverse[λ[t => Eval[G[t]]], A, B](fa)(f)(Applicative[Eval].compose[G])
}

def safeFoldLeft[A, B](fa: F[A], b: B)(f: (B, A) => Eval[B]): Eval[B] =
gen.fr.safeFoldLeft(gen.to(fa), b)(f)
protected def mkSafeFoldLeft[F[_], A, B](F: TraverseOrMk[F])(fa: F[A], b: B)(f: (B, A) => Eval[B]): Eval[B] =
F.unify match {
case mk: MkTraverse[F] => mk.safeFoldLeft(fa, b)(f)
case other => Eval.later(other.foldLeft(fa, b)(f(_, _).value))
}
}

private[derived] trait MkTraverseUtils {

protected type TraverseOrMk[F[_]] = Traverse[F] OrElse MkTraverse[F]
implicit def mkTraverseGeneric[F[_]](implicit F: Generic1[F, MkTraverse]): MkTraverse[F] =
new MkTraverse[F] {

protected def apEval[G[_] : Applicative] = Applicative[Eval].compose[G]
def safeTraverse[G[_], A, B](fa: F[A])(f: A => Eval[G[B]])(implicit G: Applicative[G]) =
F.fr.safeTraverse(F.to(fa))(f).map(G.map(_)(F.from))

protected implicit class SafeTraverse[F[_]](val F: Traverse[F]) {
def safeTraverse[G[_] : Applicative, A, B](fa: F[A])(f: A => Eval[G[B]]): Eval[G[F[B]]] = F match {
case mk: MkTraverse[F] => mk.safeTraverse(fa)(f)
case _ => F.traverse[λ[t => Eval[G[t]]], A, B](fa)(f)(apEval[G])
}
def foldRight[A, B](fa: F[A], lb: Eval[B])(f: (A, Eval[B]) => Eval[B]) =
F.fr.foldRight(F.to(fa), lb)(f)

def safeFoldLeft[A, B](fa: F[A], b: B)(f: (B, A) => Eval[B]): Eval[B] = F match {
case mff: MkTraverse[F] => mff.safeFoldLeft(fa, b)(f)
case _ => Now(F.foldLeft(fa, b) { (b, a) => f(b, a).value })
def safeFoldLeft[A, B](fa: F[A], b: B)(f: (B, A) => Eval[B]) =
F.fr.safeFoldLeft(F.to(fa), b)(f)
}
}

}
Loading

0 comments on commit 58cc026

Please sign in to comment.