-
Notifications
You must be signed in to change notification settings - Fork 67
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 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
Showing
8 changed files
with
218 additions
and
281 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} | ||
} | ||
|
||
} |
Oops, something went wrong.