diff --git a/CHANGELOG/breaking.md b/CHANGELOG/breaking.md new file mode 100644 index 00000000..b2e9f2af --- /dev/null +++ b/CHANGELOG/breaking.md @@ -0,0 +1,2 @@ +- renamed `FunctorT#map` and `TraverseT#traverse` to `mapT` and `traverseT` +- added a bunch of new operations (many pulled from Quasar) diff --git a/build.sbt b/build.sbt index 209a67f0..1f4a4e19 100644 --- a/build.sbt +++ b/build.sbt @@ -32,6 +32,7 @@ lazy val standardSettings = Seq[Setting[_]]( headers := Map( "scala" -> Apache2_0("2014–2016", "SlamData Inc."), "java" -> Apache2_0("2014–2016", "SlamData Inc.")), + scalaOrganization := "org.typelevel", scalaVersion := "2.11.8", logBuffered in Compile := false, logBuffered in Test := false, @@ -47,9 +48,8 @@ lazy val standardSettings = Seq[Setting[_]]( "JBoss repository" at "https://repository.jboss.org/nexus/content/repositories/", "Scalaz Bintray Repo" at "http://dl.bintray.com/scalaz/releases", "bintray/non" at "http://dl.bintray.com/non/maven"), - addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.2"), + addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.3"), addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full), - addCompilerPlugin("com.milessabin" % "si2712fix-plugin" % "1.2.0" cross CrossVersion.full), ScoverageKeys.coverageHighlighting := true, scalacOptions ++= Seq( @@ -67,9 +67,11 @@ lazy val standardSettings = Seq[Setting[_]]( "-Yno-imports", "-Ywarn-dead-code", // N.B. doesn't work well with the ??? hole "-Ywarn-numeric-widen", + "-Ypartial-unification", "-Ywarn-unused-import", "-Ywarn-value-discard"), - scalacOptions in (Compile,doc) ++= Seq("-groups", "-implicits"), + scalacOptions in (Compile, doc) ++= Seq("-groups", "-implicits"), + scalacOptions in (Compile, doc) -= "-Xfatal-warnings", scalacOptions in (Test, console) --= Seq( "-Yno-imports", "-Ywarn-unused-import"), @@ -85,7 +87,7 @@ lazy val standardSettings = Seq[Setting[_]]( licenses += ("Apache 2", url("http://www.apache.org/licenses/LICENSE-2.0")), checkHeaders := { - if ((createHeaders in Compile).value.nonEmpty) error("headers not all present") + if ((createHeaders in Compile).value.nonEmpty) sys.error("headers not all present") }) // Using a Seq of desired warts instead of Warts.allBut due to an incremental compilation issue. diff --git a/core/shared/src/main/scala/matryoshka/Corecursive.scala b/core/shared/src/main/scala/matryoshka/Corecursive.scala index 300554c7..804c7316 100644 --- a/core/shared/src/main/scala/matryoshka/Corecursive.scala +++ b/core/shared/src/main/scala/matryoshka/Corecursive.scala @@ -40,33 +40,33 @@ trait Corecursive[T] extends Based[T] { : M[T] = f(a).flatMap(_.traverse(anaM(_)(f))) ∘ (embed(_)) - def gana[N[_], A] + def gana[N[_]: Monad, A] (a: A) (k: DistributiveLaw[N, Base], f: GCoalgebra[N, Base, A]) - (implicit BF: Functor[Base], N: Monad[N]) + (implicit BF: Functor[Base]) : T = { - def loop(x: N[Base[N[A]]]): T = embed(k(x) ∘ (x => loop(N.lift(f)(x.join)))) + def loop(x: N[Base[N[A]]]): T = embed(k(x) ∘ (x => loop(x.join ∘ f))) loop(f(a).point[N]) } - def ganaM[N[_]: Traverse, M[_]: Monad, A]( + def ganaM[N[_]: Monad: Traverse, M[_]: Monad, A]( a: A)( k: DistributiveLaw[N, Base], f: GCoalgebraM[N, M, Base, A])( - implicit BT: Traverse[Base], N: Monad[N]): + implicit BT: Traverse[Base]): M[T] = { def loop(x: N[Base[N[A]]]): M[T] = - k(x).traverse(x => N.lift(f)(x.join).sequence >>= loop) ∘ (embed(_)) + k(x).traverse(_.join.traverse(f) >>= loop) ∘ (embed(_)) f(a) ∘ (_.point[N]) >>= loop } - def elgotAna[N[_], A]( + def elgotAna[N[_]: Monad, A]( a: A)( k: DistributiveLaw[N, Base], ψ: ElgotCoalgebra[N, Base, A])( - implicit BF: Functor[Base], N: Monad[N]): + implicit BF: Functor[Base]): T = { - def loop(x: N[Base[A]]): T = embed(k(x) ∘ (x => loop(N.lift(ψ)(x).join))) + def loop(x: N[Base[A]]): T = embed(k(x) ∘ (x => loop(x >>= ψ))) loop(ψ(a)) } @@ -117,7 +117,7 @@ trait Corecursive[T] extends Based[T] { implicit T: Recursive.Aux[T, Base], BF: Functor[Base], N: Monad[N]): T = { def loop(ma: N[A]): T = - embed(k(N.lift(ψ)(ma)) ∘ (x => ana(loop(x.join))(x => e(T.project(x))))) + embed(k(ma ∘ ψ) ∘ (x => ana(loop(x.join))(x => e(T.project(x))))) loop(a.point[N]) } diff --git a/core/shared/src/main/scala/matryoshka/FunctorT.scala b/core/shared/src/main/scala/matryoshka/FunctorT.scala index 33f819cf..58f076fe 100644 --- a/core/shared/src/main/scala/matryoshka/FunctorT.scala +++ b/core/shared/src/main/scala/matryoshka/FunctorT.scala @@ -47,49 +47,70 @@ import simulacrum._ * - `transCata` – akin to Fixplate’s `restructure` */ @typeclass trait FunctorT[T[_[_]]] { - @op("∘") def map[F[_]: Functor, G[_]: Functor](t: T[F])(f: F[T[F]] => G[T[G]]): + def mapT[F[_]: Functor, G[_]: Functor](t: T[F])(f: F[T[F]] => G[T[G]]): T[G] def transCataT[F[_]: Functor](t: T[F])(f: T[F] => T[F]): T[F] = - f(map(t)(_.map(transCataT(_)(f)))) + f(mapT(t)(_.map(transCataT(_)(f)))) + + /** This behaves like [[matryoshka.Recursive.elgotPara]]`, but it’s harder to + * see from the types that in the tuple, `_2` is the result so far and `_1` + * is the original structure. + */ + def transParaT[F[_]: Functor](t: T[F])(f: ((T[F], T[F])) => T[F]): T[F] = + f((t, mapT(t)(_.map(transParaT(_)(f))))) def transAnaT[F[_]: Functor](t: T[F])(f: T[F] => T[F]): T[F] = - map(f(t))(_.map(transAnaT(_)(f))) + mapT(f(t))(_.map(transAnaT(_)(f))) + /** This behaves like [[matryoshka.Corecursive.elgotApo]]`, but it’s harder to + * see from the types that in the disjunction, `-\/` is the final result for + * this node, while `\/-` means to keep processing the children. + */ def transApoT[F[_]: Functor](t: T[F])(f: T[F] => T[F] \/ T[F]): T[F] = - f(t).fold(Predef.identity, map(_)(_.map(transApoT(_)(f)))) + f(t).fold(Predef.identity, mapT(_)(_.map(transApoT(_)(f)))) + + def transHyloT[F[_]: Functor](t: T[F])(φ: T[F] => T[F], ψ: T[F] => T[F]): + T[F] = + φ(mapT(ψ(t))(_ ∘ (transHyloT(_)(φ, ψ)))) def transCata[F[_]: Functor, G[_]: Functor](t: T[F])(f: AlgebraicTransform[T, F, G]): T[G] = - map(t)(ft => f(ft.map(transCata(_)(f)))) + mapT(t)(ft => f(ft.map(transCata(_)(f)))) def transAna[F[_]: Functor, G[_]: Functor](t: T[F])(f: CoalgebraicTransform[T, F, G]): T[G] = - map(t)(f(_).map(transAna(_)(f))) + mapT(t)(f(_).map(transAna(_)(f))) def transPrepro[F[_]: Functor, G[_]: Functor](t: T[F])(e: F ~> F, f: AlgebraicTransform[T, F, G]): T[G] = - map(t)(ft => f(ft ∘ (x => transPrepro(transCata[F, F](x)(e(_)))(e, f)))) + mapT(t)(ft => f(ft ∘ (x => transPrepro(transCata[F, F](x)(e(_)))(e, f)))) def transPostpro[F[_]: Functor, G[_]: Functor](t: T[F])(e: G ~> G, f: CoalgebraicTransform[T, F, G]): T[G] = - map(t)(f(_) ∘ (x => transAna(transPostpro(x)(e, f))(e))) + mapT(t)(f(_) ∘ (x => transAna(transPostpro(x)(e, f))(e))) def transPara[F[_]: Functor, G[_]: Functor](t: T[F])(f: GAlgebraicTransform[T, (T[F], ?), F, G]): T[G] = - map(t)(ft => f(ft.map(tf => (tf, transPara(tf)(f))))) + mapT(t)(ft => f(ft.map(tf => (tf, transPara(tf)(f))))) def transApo[F[_]: Functor, G[_]: Functor](t: T[F])(f: GCoalgebraicTransform[T, (T[G] \/ ?), F, G]): T[G] = - map(t)(f(_).map(_.fold(Predef.identity, transApo(_)(f)))) + mapT(t)(f(_).map(_.fold(Predef.identity, transApo(_)(f)))) + + def transHylo[F[_]: Functor, G[_]: Functor, H[_]: Functor]( + t: T[F])( + φ: G[T[H]] => H[T[H]], ψ: F[T[F]] => G[T[F]]): + T[H] = + mapT(t)(ft => φ(ψ(ft) ∘ (transHylo(_)(φ, ψ)))) def topDownCata[F[_]: Functor, A](t: T[F], a: A)(f: (A, T[F]) => (A, T[F])): T[F] = { val (a0, tf) = f(a, t) - map(tf)(_.map(topDownCata(_, a0)(f))) + mapT(tf)(_.map(topDownCata(_, a0)(f))) } } object FunctorT { - implicit def birecursiveTFunctorT[T[_[_]]: RecursiveT: CorecursiveT]: FunctorT[T] = + implicit def birecursiveT[T[_[_]]: RecursiveT: CorecursiveT]: FunctorT[T] = new FunctorT[T] { - def map[F[_], G[_]](t: T[F])(f: F[T[F]] => G[T[G]])(implicit F: Functor[F], G: Functor[G]) = + def mapT[F[_], G[_]](t: T[F])(f: F[T[F]] => G[T[G]])(implicit F: Functor[F], G: Functor[G]) = CorecursiveT[T].embedT(f(RecursiveT[T].projectT(t))) } } diff --git a/core/shared/src/main/scala/matryoshka/Recursive.scala b/core/shared/src/main/scala/matryoshka/Recursive.scala index 8bcac643..bd93971b 100644 --- a/core/shared/src/main/scala/matryoshka/Recursive.scala +++ b/core/shared/src/main/scala/matryoshka/Recursive.scala @@ -304,8 +304,6 @@ trait Recursive[T] extends Based[T] { } object Recursive { - type Aux[T, F[_]] = Recursive[T] { type Base[A] = F[A] } - def equal[T, F[_]: Functor] (implicit T: Recursive.Aux[T, F], F: Delay[Equal, F]) : Equal[T] = @@ -318,6 +316,8 @@ object Recursive { // NB: The rest of this is what would be generated by simulacrum, except this // type class is too complicated to take advantage of that. + type Aux[T, F[_]] = Recursive[T] { type Base[A] = F[A] } + def apply[T, F[_]](implicit instance: Aux[T, F]): Aux[T, F] = instance trait Ops[T, F[_]] { diff --git a/core/shared/src/main/scala/matryoshka/TraverseT.scala b/core/shared/src/main/scala/matryoshka/TraverseT.scala index 79239a39..9f788e7d 100644 --- a/core/shared/src/main/scala/matryoshka/TraverseT.scala +++ b/core/shared/src/main/scala/matryoshka/TraverseT.scala @@ -20,37 +20,49 @@ import scalaz._, Scalaz._ import simulacrum._ @typeclass trait TraverseT[T[_[_]]] extends FunctorT[T] { - def traverse[M[_]: Applicative, F[_]: Functor, G[_]: Functor](t: T[F])(f: F[T[F]] => M[G[T[G]]]): - M[T[G]] + def traverseT[M[_]: Applicative, F[_]: Functor, G[_]: Functor] + (t: T[F]) + (f: F[T[F]] => M[G[T[G]]]) + : M[T[G]] - def map[F[_]: Functor, G[_]: Functor](t: T[F])(f: F[T[F]] => G[T[G]]) = traverse[Id, F, G](t)(f) + override def mapT[F[_]: Functor, G[_]: Functor] + (t: T[F]) + (f: F[T[F]] => G[T[G]]) = + traverseT[Id, F, G](t)(f) - def transCataTM[F[_]: Traverse, M[_]: Monad](t: T[F])(f: T[F] => M[T[F]]): M[T[F]] = - traverse(t)(_.traverse(transCataTM(_)(f))).flatMap(f) + def transCataTM[F[_]: Traverse, M[_]: Monad](t: T[F])(f: T[F] => M[T[F]]) + : M[T[F]] = + traverseT(t)(_.traverse(transCataTM(_)(f))).flatMap(f) - def transAnaTM[F[_]: Traverse, M[_]: Monad](t: T[F])(f: T[F] => M[T[F]]): - M[T[F]] = - f(t).flatMap(traverse(_)(_.traverse(transAnaTM(_)(f)))) + def transAnaTM[F[_]: Traverse, M[_]: Monad](t: T[F])(f: T[F] => M[T[F]]) + : M[T[F]] = + f(t).flatMap(traverseT(_)(_.traverse(transAnaTM(_)(f)))) - def transCataM[M[_]: Monad, F[_]: Traverse, G[_]: Functor](t: T[F])(f: AlgebraicTransformM[T, M, F, G]): M[T[G]] = - traverse(t)(_.traverse(transCataM(_)(f)).flatMap(f)) + def transCataM[M[_]: Monad, F[_]: Traverse, G[_]: Functor] + (t: T[F]) + (f: AlgebraicTransformM[T, M, F, G]) + : M[T[G]] = + traverseT(t)(_.traverse(transCataM(_)(f)).flatMap(f)) - def transAnaM[M[_]: Monad, F[_]: Functor, G[_]: Traverse](t: T[F])(f: CoalgebraicTransformM[T, M, F, G]): M[T[G]] = - traverse(t)(f(_).flatMap(_.traverse(transAnaM(_)(f)))) + def transAnaM[M[_]: Monad, F[_]: Functor, G[_]: Traverse] + (t: T[F]) + (f: CoalgebraicTransformM[T, M, F, G]) + : M[T[G]] = + traverseT(t)(f(_).flatMap(_.traverse(transAnaM(_)(f)))) def topDownCataM[F[_]: Traverse, M[_]: Monad, A]( t: T[F], a: A)( f: (A, T[F]) => M[(A, T[F])]): M[T[F]] = f(a, t).flatMap { case (a, tf) => - traverse(tf)(_.traverse(topDownCataM(_, a)(f))) + traverseT(tf)(_.traverse(topDownCataM(_, a)(f))) } } object TraverseT { - implicit def birecursiveTTraverseT[T[_[_]]: RecursiveT: CorecursiveT]: TraverseT[T] = + implicit def birecursiveT[T[_[_]]: RecursiveT: CorecursiveT]: TraverseT[T] = new TraverseT[T] { - def traverse[M[_]: Applicative, F[_]: Functor, G[_]: Functor] + def traverseT[M[_]: Applicative, F[_]: Functor, G[_]: Functor] (t: T[F]) (f: F[T[F]] => M[G[T[G]]]) = f(RecursiveT[T].projectT(t)).map(CorecursiveT[T].embedT[G]) diff --git a/core/shared/src/main/scala/matryoshka/data/Mu.scala b/core/shared/src/main/scala/matryoshka/data/Mu.scala index d6381a18..72ced114 100644 --- a/core/shared/src/main/scala/matryoshka/data/Mu.scala +++ b/core/shared/src/main/scala/matryoshka/data/Mu.scala @@ -23,7 +23,7 @@ import scalaz._, Scalaz._ /** This is for inductive (finite) recursive structures, models the concept of * “data”, aka, the “least fixed point”. */ -final case class Mu[F[_]](unMu: λ[A => Algebra[F, A]] ~> Id) +final case class Mu[F[_]](unMu: Algebra[F, ?] ~> Id) object Mu { implicit def recursiveT: RecursiveT[Mu] = new RecursiveT[Mu] { // FIXME: ugh, shouldn’t have to redefine `lambek` in here? @@ -34,8 +34,8 @@ object Mu { implicit def corecursiveT: CorecursiveT[Mu] = new CorecursiveT[Mu] { def embedT[F[_]: Functor](t: F[Mu[F]]) = - Mu(new (λ[A => (F[A] => A)] ~> Id) { - def apply[A](fa: F[A] => A): A = fa(t.map(recursiveT.cataT(_)(fa))) + Mu(new (Algebra[F, ?] ~> Id) { + def apply[A](fa: Algebra[F, A]): A = fa(t.map(recursiveT.cataT(_)(fa))) }) } diff --git a/core/shared/src/main/scala/matryoshka/data/cofree.scala b/core/shared/src/main/scala/matryoshka/data/cofree.scala index 9d6287af..ba0ffdc0 100644 --- a/core/shared/src/main/scala/matryoshka/data/cofree.scala +++ b/core/shared/src/main/scala/matryoshka/data/cofree.scala @@ -20,7 +20,6 @@ import matryoshka._ import matryoshka.patterns.EnvT import scalaz._ -import scalaz.syntax.functor._ trait CofreeInstances { implicit def cofreeRecursive[F[_], A]: Recursive.Aux[Cofree[F, A], EnvT[A, F, ?]] = @@ -39,12 +38,6 @@ trait CofreeInstances { Cofree(t.ask, t.lower) } - implicit def cofreeTraverseT[A]: TraverseT[Cofree[?[_], A]] = - new TraverseT[Cofree[?[_], A]] { - def traverse[M[_]: Applicative, F[_]: Functor, G[_]: Functor](t: Cofree[F, A])(f: F[Cofree[F, A]] => M[G[Cofree[G, A]]]) = - f(t.tail).map(Cofree(t.head, _)) - } - implicit def cofreeEqual[F[_]](implicit F: Delay[Equal, F]): Delay[Equal, Cofree[F, ?]] = new Delay[Equal, Cofree[F, ?]] { diff --git a/core/shared/src/main/scala/matryoshka/data/free.scala b/core/shared/src/main/scala/matryoshka/data/free.scala index efeb1b0e..e3bca5d9 100644 --- a/core/shared/src/main/scala/matryoshka/data/free.scala +++ b/core/shared/src/main/scala/matryoshka/data/free.scala @@ -39,14 +39,6 @@ trait FreeInstances { t.run.fold(_.point[Free[F, ?]], Free.liftF(_).join) } - implicit def freeTraverseT[A]: TraverseT[Free[?[_], A]] = - new TraverseT[Free[?[_], A]] { - def traverse[M[_]: Applicative, F[_]: Functor, G[_]: Functor](t: Free[F, A])(f: F[Free[F, A]] => M[G[Free[G, A]]]) = - t.fold( - _.point[Free[G, ?]].point[M], - f(_).map(Free.liftF(_).join)) - } - implicit def freeEqual[F[_]: Functor](implicit F: Delay[Equal, F]): Delay[Equal, Free[F, ?]] = new Delay[Equal, Free[F, ?]] { diff --git a/core/shared/src/main/scala/matryoshka/data/package.scala b/core/shared/src/main/scala/matryoshka/data/package.scala index 35be106f..58428fdf 100644 --- a/core/shared/src/main/scala/matryoshka/data/package.scala +++ b/core/shared/src/main/scala/matryoshka/data/package.scala @@ -20,6 +20,13 @@ import matryoshka.implicits._ import scalaz._, Scalaz._ +/** This packages contains fixed-point operators as well as instances of + * recursion schemes for various extant data types. + * + * The reason these are relegated to their own package is because, in general, + * you should eschew using them directly, but rather rely on the type class + * constraints, and only require specific types at the boundaries. + */ package object data extends CofreeInstances with EitherInstances @@ -28,11 +35,6 @@ package object data with IListInstances with MaybeInstances { - // final class CofParaMPartiallyApplied[M[_]] { self => - // def apply[T[_[_]]: Corecursive, S[_]: Traverse, A, B](t: Cofree[S, A])(f: (A, S[(T[S], B)]) => M[B])(implicit M: Monad[M]): M[B] = - // t.tail.traverse(cs => self(cs)(f) ∘ ((Recursive[Cofree[?[_], A]].convertTo[S, T](cs), _))) >>= (f(t.head, _)) - // } - /** NB: Since Cofree carries the functor, the resulting algebra is a cata, not * a para. * diff --git a/core/shared/src/main/scala/matryoshka/implicits/IdOps.scala b/core/shared/src/main/scala/matryoshka/implicits/IdOps.scala index 721ae6c0..455de081 100644 --- a/core/shared/src/main/scala/matryoshka/implicits/IdOps.scala +++ b/core/shared/src/main/scala/matryoshka/implicits/IdOps.scala @@ -115,6 +115,18 @@ sealed class IdOps[A](self: A) { } } + object ganaM { + def apply[T] = new Aux[T] + + final class Aux[T] { + def apply[N[_]: Monad: Traverse, M[_]: Monad, F[_]: Traverse] + (k: DistributiveLaw[N, F], f: GCoalgebraM[N, M, F, A]) + (implicit T: Corecursive.Aux[T, F]) + : M[T] = + T.ganaM(self)(k, f) + } + } + object elgotAna { def apply[T] = new Aux[T] diff --git a/core/shared/src/main/scala/matryoshka/instances/fixedpoint/package.scala b/core/shared/src/main/scala/matryoshka/instances/fixedpoint/package.scala index b62d4a54..26bc3ddc 100644 --- a/core/shared/src/main/scala/matryoshka/instances/fixedpoint/package.scala +++ b/core/shared/src/main/scala/matryoshka/instances/fixedpoint/package.scala @@ -31,15 +31,19 @@ import scalaz._, Scalaz._ * implemented explicitly as fixed-points. */ package object fixedpoint { + + /** Natural numbers represented as the least fixed-point of [[scala.Option]]. + */ type Nat = Mu[Option] + object Nat { def fromInt: CoalgebraM[Option, Option, Int] = x => if (x < 0) None else Some(if (x > 0) (x - 1).some else None) // NB: This isn’t defined via `AlgebraPrism` because it only holds across a // recursive structure. - def intPrism = - Prism[Int, Fix[Option]](_.anaM[Fix[Option]](fromInt))(_.cata(height)) + def intPrism[T](implicit TR: Recursive.Aux[T, Option], TC: Corecursive.Aux[T, Option]) = + Prism[Int, T](_.anaM[T](fromInt))(_.cata(height)) } implicit class NatOps[T] @@ -65,8 +69,10 @@ package object fixedpoint { }) } + /** The dual of [[Nat]], a potentially-infinite number. */ type Conat = Nu[Option] object Conat { + /** A representation of infinity, as a non-terminating corecursive process */ val inf: Conat = ().ana[Nu[Option]](_.some) } @@ -77,35 +83,37 @@ package object fixedpoint { def apply[A](elems: A*) = elems.ana[Mu[ListF[A, ?]]](ListF.seqIso[A].reverseGet) - def fillƒ[A](elem: => A): Option ~> ListF[A, ?] = - new (Option ~> ListF[A, ?]) { - def apply[β](opt: Option[β]) = opt match { - case None => NilF() - case Some(b) => ConsF(elem, b) - } - } + def tuple[A](elem: => A) = λ[Option ~> ListF[A, ?]] { + case None => NilF() + case Some(b) => ConsF(elem, b) + } - def fill[A](n: Int)(elem: => A): List[A] = - n.hyloM( - transformToAlgebra[Mu, Id, Option, Option, ListF[A, ?]](fillƒ(elem).apply(_).point[Option]), - Nat.fromInt) - .getOrElse((NilF[A, List[A]](): ListF[A, List[A]]).embed) - } + def forget[A] = λ[ListF[A, ?] ~> Option] { + case NilF() => None + case ConsF(_, t) => t.some + } - implicit class ListOps[A](self: Mu[ListF[A, ?]]) { - def length: Int = self.cata(size) - 1 - def headOption: Option[A] = self.project.headOption - def tailOption: Option[List[A]] = self.project.tailOption + def fill[A](n: Nat)(elem: => A): List[A] = n.transAna(tuple(elem)) } + // FIXME: This implicit conversion seems to not get found, so we specialize + // `T` below. implicit class RecListFOps[T, A] (self: T) (implicit T: Recursive.Aux[T, ListF[A, ?]]) { + def find(cond: A => Boolean): Option[A] = self.cata(ListF.find(cond)) def length: Int = self.cata(size) - 1 def headOption: Option[A] = self.project.headOption def tailOption: Option[T] = self.project.tailOption + def take[N] + (i: N) + (implicit N: Recursive.Aux[N, Option], C: Corecursive.Aux[T, ListF[A, ?]]) + : T = + (i, self).ana[T](ListF.takeUpTo) } + implicit def ListOps[A](self: List[A]) = new RecListFOps[List[A], A](self) + /** A lazy (potentially-infinite) list. */ type Colist[A] = Nu[ListF[A, ?]] @@ -114,17 +122,56 @@ package object fixedpoint { */ type Stream[A] = Nu[(A, ?)] + object Stream { + def matchesFirst[A, B](cond: A => Boolean) = + λ[(A, ?) ~> (A \/ ?)] { + case (h, t) => if (cond(h)) h.left else t.right + } + + def take[N, T, A](implicit N: Recursive.Aux[N, Option], T: Recursive.Aux[T, (A, ?)]): Coalgebra[ListF[A, ?], (N, T)] = { + case (n, s) => + n.project.fold[ListF[A, (N, T)]]( + NilF())( + prev => { + val pair = s.project + ConsF(pair._1, (prev, pair._2)) + }) + } + + /** Colists are simply streams that may terminate, so a stream is easily + * converted to a Colist that doesn’t terminate. + */ + // TODO: This could be `toConsF` potentially. + def toListF[A] = λ[(A, ?) ~> ListF[A, ?]](p => ConsF(p._1, p._2)) + } + implicit class StreamOps[A](self: Nu[(A, ?)]) { def head: A = self.project._1 + def tail: Nu[(A, ?)] = self.project._2 - @tailrec final def drop(n: Int): Nu[(A, ?)] = - if (n > 0) tail.drop(n - 1) else self - def take(n: Int): Mu[ListF[A, ?]] = - (n, self).ana[Mu[ListF[A, ?]]] { - case (r, stream) if (r > 0) => - ConsF(stream.head, (r - 1, stream.tail)): ListF[A, (Int, Nu[(A, ?)])] - case (_, _) => NilF(): ListF[A, (Int, Nu[(A, ?)])] - } + + /** Drops exactly `n` elements from the stream. + * This doesn’t expose the Coalgebra because it returns `Stream \/ Stream`, + * which isn’t the type of `drop`. + */ + def drop[N](n: N)(implicit N: Recursive.Aux[N, Option]): Nu[(A, ?)] = + (n, self).anaM[Stream[A]] { + case (r, stream) => + r.project.fold[Stream[A] \/ (A, (N, Stream[A]))]( + stream.left)( + prev => stream.project.map((prev, _)).right) + }.merge + + def take[N, T] + (n: N) + (implicit N: Recursive.Aux[N, Option], T: Corecursive.Aux[T, ListF[A, ?]]) + : T = + (n, self).ana[T](Stream.take[N, Nu[(A, ?)], A]) + + /** Colists are simply streams that may terminate, so a stream is easily + * converted to a Colist that doesn’t terminate. + */ + def toColist: Nu[ListF[A, ?]] = self.transAna(Stream.toListF(_)) } /** Encodes a function that may diverge. @@ -132,12 +179,13 @@ package object fixedpoint { type Partial[A] = Nu[A \/ ?] object Partial { + /** A partial function that immediately evaluates to the provided value. + */ def now[A](a: A): Partial[A] = a.left[Nu[A \/ ?]].embed + def later[A](partial: Partial[A]): Partial[A] = partial.right[A].embed - def delay[A](a: A): Option ~> (A \/ ?) = new (Option ~> (A \/ ?)) { - def apply[B](b: Option[B]) = b \/> a - } + def delay[A](a: A): Option ~> (A \/ ?) = λ[Option ~> (A \/ ?)](_ \/> a) /** Canonical function that diverges. */ @@ -149,6 +197,7 @@ package object fixedpoint { Equal.equal((a, b) => (a ≈ b).unsafePerformSync) def fromOption[A](opt: Option[A]): Partial[A] = opt.fold(never[A])(now) + def fromPartialFunction[A, B](pf: scala.PartialFunction[A, B]): A => Partial[B] = pf.lift ⋙ fromOption @@ -156,13 +205,12 @@ package object fixedpoint { implicit val partialMonad: Monad[Partial] = new Monad[Partial] { def point[A](a: => A) = Partial.now(a) + def bind[A, B](fa: Partial[A])(f: A => Partial[B]) = fa.project.fold(f, l => Partial.later(bind(l)(f))) } implicit class PartialOps[A](self: Nu[A \/ ?]) { - import Partial._ - def step: A \/ Partial[A] = self.project /** Returns `left` if the result was found within the given number of steps. @@ -177,16 +225,14 @@ package object fixedpoint { case \/-(p) => p.unsafePerformSync } + // TODO: Would be nice to have this in ApplicativeOps + def almostEqual[F[_]: Applicative, A: Equal](a: F[A], b: F[A]): F[Boolean] = + (a ⊛ b)(_ ≟ _) + /** If two `Partial`s eventually have the same value, then they are * equivalent. */ def ≈(that: Partial[A])(implicit A: Equal[A]): Partial[Boolean] = - // NB: could be defined as `(self ⊛ that)(_ ≟ _)` - (self, that).ana[Nu[Boolean \/ ?]](_.bimap(_.project, _.project) match { - case (-\/(a1), -\/(a2)) => (a1 ≟ a2).left - case (\/-(l1), -\/(a2)) => (l1, now(a2)).right - case (-\/(a1), \/-(l2)) => (now(a1), l2).right - case (\/-(l1), \/-(l2)) => (l1, l2).right - }) + almostEqual[Partial, A](self, that) } } diff --git a/core/shared/src/main/scala/matryoshka/package.scala b/core/shared/src/main/scala/matryoshka/package.scala index 1fd35f46..964b6300 100644 --- a/core/shared/src/main/scala/matryoshka/package.scala +++ b/core/shared/src/main/scala/matryoshka/package.scala @@ -18,7 +18,7 @@ import matryoshka.implicits._ import matryoshka.patterns.{CoEnv, EnvT} import scala.{Boolean, Function, Int, None, Option, Unit} -import scala.collection.immutable.{List, ::} +import scala.collection.immutable.{List, Nil, ::} import monocle._ import scalaz._, Liskov._, Scalaz._ @@ -140,12 +140,26 @@ package object matryoshka { : GAlgebraM[W, M, F, T[G]] = self(_) ∘ (_.embed) + /** @group algtrans */ + def transformToCoalgebra[T[_[_]], N[_], M[_], F[_]: Functor, G[_]] + (self: GCoalgebraicTransformM[T, N, M, F, G]) + (implicit T: Recursive.Aux[T[F], F]) + : GCoalgebraM[N, M, G, T[F]] = + x => self(x.project) + /** An algebra and its dual form an isomorphism. */ type GAlgebraIso[W[_], N[_], F[_], A] = PIso[F[W[A]], F[N[A]], A, A] object GAlgebraIso { - def apply[W[_], N[_], F[_], A](φ: GAlgebra[W, F, A])(ψ: GCoalgebra[N, F, A]): - GAlgebraIso[W, N, F, A] = + def apply[W[_], N[_], F[_], A](φ: GAlgebra[W, F, A])(ψ: GCoalgebra[N, F, A]) + : GAlgebraIso[W, N, F, A] = + PIso(φ)(ψ) + } + + type ElgotAlgebraIso[W[_], N[_], F[_], A] = PIso[W[F[A]], N[F[A]], A, A] + object ElgotAlgebraIso { + def apply[W[_], N[_], F[_], A](φ: ElgotAlgebra[W, F, A])(ψ: ElgotCoalgebra[N, F, A]) + : ElgotAlgebraIso[W, N, F, A] = PIso(φ)(ψ) } @@ -247,15 +261,14 @@ package object matryoshka { * * @group refolds */ - def ghylo[W[_]: Comonad, N[_], F[_]: Functor, A, B]( + def ghylo[W[_]: Comonad, N[_]: Monad, F[_]: Functor, A, B]( a: A)( w: DistributiveLaw[F, W], n: DistributiveLaw[N, F], f: GAlgebra[W, F, B], - g: GCoalgebra[N, F, A])( - implicit N: Monad[N]): + g: GCoalgebra[N, F, A]): B = { - def h(x: N[A]): W[B] = w(n(N.lift(g)(x)) ∘ (y => h(y.join).cojoin)) ∘ f + def h(x: N[A]): W[B] = w(n(x ∘ g) ∘ (y => h(y.join).cojoin)) ∘ f h(a.point[N]).copoint } @@ -263,16 +276,15 @@ package object matryoshka { * * @group refolds */ - def ghyloM[W[_]: Comonad: Traverse, N[_]: Traverse, M[_]: Monad, F[_]: Traverse, A, B]( + def ghyloM[W[_]: Comonad: Traverse, N[_]: Monad: Traverse, M[_]: Monad, F[_]: Traverse, A, B]( a: A)( w: DistributiveLaw[F, W], m: DistributiveLaw[N, F], f: GAlgebraM[W, M, F, B], - g: GCoalgebraM[N, M, F, A])( - implicit N: Monad[N]): + g: GCoalgebraM[N, M, F, A]): M[B] = { def h(x: N[A]): M[W[B]] = - (N.lift(g)(x).sequence >>= + (x.traverse(g) >>= (m(_: N[F[N[A]]]).traverse(y => h(y.join) ∘ (_.cojoin)))) ∘ (w(_)) >>= (_.traverse(f)) @@ -358,6 +370,26 @@ package object matryoshka { */ def distCata[F[_]]: DistributiveLaw[F, Id] = NaturalTransformation.refl + /** A general [[DistributiveLaw]] for the case where the [[scalaz.Comonad]] is + * also [[scalaz.Applicative]]. + * + * @group dist + */ + def distApplicative[F[_]: Traverse, G[_]: Applicative] = + new DistributiveLaw[F, G] { + def apply[A](fga: F[G[A]]) = fga.sequence + } + + /** A general [[DistributiveLaw]] for the case where the [[scalaz.Comonad]] is + * also [[scalaz.Distributive]]. + * + * @group dist + */ + def distDistributive[F[_]: Functor, G[_]: Distributive] = + new DistributiveLaw[F, G] { + def apply[A](fga: F[G[A]]) = fga.cosequence + } + /** * * @group dist @@ -371,14 +403,11 @@ package object matryoshka { * * @group dist */ - def distZygoT[F[_], W[_]: Comonad, B]( - g: Algebra[F, B], k: DistributiveLaw[F, W])( - implicit F: Functor[F]) = + def distZygoT[F[_]: Functor, W[_]: Comonad, B]( + g: Algebra[F, B], k: DistributiveLaw[F, W]) = new DistributiveLaw[F, EnvT[B, W, ?]] { def apply[α](fe: F[EnvT[B, W, α]]) = - EnvT(( - g(F.lift[EnvT[B, W, α], B](_.ask)(fe)), - k(F.lift[EnvT[B, W, α], W[α]](_.lower)(fe)))) + EnvT((g(fe ∘ (_.ask)), k(fe ∘ (_.lower)))) } /** @@ -398,14 +427,10 @@ package object matryoshka { // TODO: Should be able to generalize this over `Recursive.Aux[T, EnvT[…]]` // somehow, then it wouldn’t depend on Scalaz and would work with any // `Cofree` representation. - def distGHisto[F[_], H[_]]( - k: DistributiveLaw[F, H])( - implicit F: Functor[F], H: Functor[H]) = + def distGHisto[F[_]: Functor, H[_]: Functor](k: DistributiveLaw[F, H]) = new DistributiveLaw[F, Cofree[H, ?]] { def apply[α](m: F[Cofree[H, α]]) = - Cofree.unfold(m)(as => ( - F.lift[Cofree[H, α], α](_.copure)(as), - k(F.lift[Cofree[H, α], H[Cofree[H, α]]](_.tail)(as)))) + Cofree.unfold(m)(as => (as ∘ (_.copure), k(as ∘ (_.tail)))) } /** @@ -460,14 +485,15 @@ package object matryoshka { // TODO: Should be able to generalize this over `Recursive.Aux[T, CoEnv[…]]` // somehow, then it wouldn’t depend on Scalaz and would work with any // `Free` representation. - def distGFutu[H[_], F[_]]( - k: DistributiveLaw[H, F])( - implicit H: Functor[H], F: Functor[F]): DistributiveLaw[Free[H, ?], F] = + def distGFutu[H[_]: Functor, F[_]] + (k: DistributiveLaw[H, F]) + (implicit F: Functor[F]) + : DistributiveLaw[Free[H, ?], F] = new DistributiveLaw[Free[H, ?], F] { def apply[α](m: Free[H, F[α]]) = m.fold( F.lift(Free.point[H, α](_)), - as => F.lift(Free.liftF(_: H[Free[H, α]]).join)(k(H.lift(distGFutu(k)(H, F)(_: Free[H, F[α]]))(as)))) + as => k(as ∘ (distGFutu(k).apply)) ∘ (Free.liftF(_).join)) } sealed trait Hole @@ -631,7 +657,7 @@ package object matryoshka { def orDefault[A, B](default: B)(f: A => Option[B]): A => B = expr => f(expr).getOrElse(default) - /** Count the instinces of `form` in the structure. + /** Count the instances of `form` in the structure. * * @group algebras */ @@ -688,6 +714,24 @@ package object matryoshka { (c, (a2, c)) } + def partition[A: Order]: Coalgebra[λ[α => Option[(A, (α, α))]], List[A]] = { + case Nil => None + case h :: t => (h, (t.filter(_ <= h), t.filter(_ > h))).some + } + + def join[A]: Algebra[λ[α => Option[(A, (α, α))]], List[A]] = + _.fold[List[A]](Nil) { case (a, (x, y)) => x ++ (a :: y) } + + // NB: not in-place + def quicksort[A: Order](as: List[A]): List[A] = { + implicit val F: Functor[λ[α => Option[(A, (α, α))]]] = + new Functor[λ[α => Option[(A, (α, α))]]] { + def map[B, C] (fa: Option[(A, (B, B))])(f: B => C) = + fa.map(_.map(_.bimap(f, f))) + } + as.hylo[λ[α => Option[(A, (α, α))]], List[A]](join, partition) + } + /** Converts a fixed-point structure into a generic Tree. * One use of this is using `.cata(toTree).drawTree` rather than `.show` to * get a pretty-printed tree. @@ -730,4 +774,19 @@ package object matryoshka { implicit def delayShow[F[_], A](implicit A: Show[A], F: Delay[Show, F]): Show[F[A]] = F(A) + + implicit def functorTFunctor[T[_[_]], F[_, _]](implicit T: FunctorT[T], F: Bifunctor[F]): Functor[λ[α => T[F[α, ?]]]] = + new Functor[λ[α => T[F[α, ?]]]] { + def map[A, B](fa: T[F[A, ?]])(f: A => B) = + T.transCata[F[A, ?], F[B, ?]](fa)(_.leftMap[B](f))(F.rightFunctor, F.rightFunctor) + } + + implicit def recursiveTFoldable[T[_[_]], F[_, _]](implicit T: RecursiveT[T], FB: Bifoldable[F], FF: Bifunctor[F]): Foldable[λ[α => T[F[α, ?]]]] = + new Foldable[λ[α => T[F[α, ?]]]] { + def foldMap[A, B: Monoid](fa: T[F[A, ?]])(f: A ⇒ B) = + T.cataT[F[A, ?], B](fa)(FB.leftFoldable.foldMap(_)(f))(FF.rightFunctor) + + def foldRight[A, B](fa: T[F[A, ?]], z: ⇒ B)(f: (A, ⇒ B) ⇒ B) = + T.cataT[F[A, ?], B](fa)(FB.leftFoldable.foldRight(_, z)(f))(FF.rightFunctor) + } } diff --git a/core/shared/src/main/scala/matryoshka/patterns/CoEnv.scala b/core/shared/src/main/scala/matryoshka/patterns/CoEnv.scala index 4f6b93cf..db5c64e3 100644 --- a/core/shared/src/main/scala/matryoshka/patterns/CoEnv.scala +++ b/core/shared/src/main/scala/matryoshka/patterns/CoEnv.scala @@ -22,9 +22,17 @@ import scalaz._, Scalaz._ /** The pattern functor for Free. */ final case class CoEnv[E, F[_], A](run: E \/ F[A]) + object CoEnv extends CoEnvInstances { def coEnv[E, F[_], A](v: E \/ F[A]): CoEnv[E, F, A] = CoEnv(v) + def hmap[F[_], G[_], A](f: F ~> G) = + λ[CoEnv[A, F, ?] ~> CoEnv[A, G, ?]](fa => CoEnv(fa.run.map(f(_)))) + + def htraverse[G[_]: Applicative, F[_], H[_], A](f: F ~> (G ∘ H)#λ) = + λ[CoEnv[A, F, ?] ~> (G ∘ CoEnv[A, H, ?])#λ]( + _.run.traverse(f(_)).map(CoEnv(_))) + def freeIso[E, F[_]: Functor] = AlgebraIso[CoEnv[E, F, ?], Free[F, E]]( coe => coe.run.fold(_.point[Free[F, ?]], Free.roll))( fr => CoEnv(fr.fold(_.left, _.right))) diff --git a/core/shared/src/main/scala/matryoshka/patterns/EnvT.scala b/core/shared/src/main/scala/matryoshka/patterns/EnvT.scala index cd21de3b..490ccb82 100644 --- a/core/shared/src/main/scala/matryoshka/patterns/EnvT.scala +++ b/core/shared/src/main/scala/matryoshka/patterns/EnvT.scala @@ -36,14 +36,13 @@ final case class EnvT[E, W[_], A](run: (E, W[A])) { self => } object EnvT extends EnvTInstances with EnvTFunctions { - def hmap[F[_], G[_], E, A](f: F ~> G): EnvT[E, F, ?] ~> EnvT[E, G, ?] = - new (EnvT[E, F, ?] ~> EnvT[E, G, ?]) { - def apply[A](env: EnvT[E, F, A]) = EnvT((env.ask, f(env.lower))) - } + def hmap[F[_], G[_], E, A](f: F ~> G) = + λ[EnvT[E, F, ?] ~> EnvT[E, G, ?]](env => EnvT((env.ask, f(env.lower)))) + + def htraverse[G[_]: Applicative, F[_], H[_], A](f: F ~> (G ∘ H)#λ) = + λ[EnvT[A, F, ?] ~> (G ∘ EnvT[A, H, ?])#λ](_.run.traverse(f(_)).map(EnvT(_))) - def lower[F[_], E]: EnvT[E, F, ?] ~> F = new (EnvT[E, F, ?] ~> F) { - def apply[A](fa: EnvT[E, F, A]): F[A] = fa.lower - } + def lower[F[_], E] = λ[EnvT[E, F, ?] ~> F](_.lower) } sealed abstract class EnvTInstances1 { diff --git a/core/shared/src/main/scala/matryoshka/patterns/ListF.scala b/core/shared/src/main/scala/matryoshka/patterns/ListF.scala index 161d43e5..1381d465 100644 --- a/core/shared/src/main/scala/matryoshka/patterns/ListF.scala +++ b/core/shared/src/main/scala/matryoshka/patterns/ListF.scala @@ -16,9 +16,9 @@ package matryoshka.patterns -import matryoshka._ +import matryoshka._, Recursive.ops._ -import scala.{List, Nil, None, Option, Seq, ::} +import scala.{Boolean, List, Nil, None, Option, Seq, ::} import scalaz._, Scalaz._ @@ -52,6 +52,14 @@ object ListF { case NilF() => Seq.empty } (s => s.headOption.fold[ListF[A, Seq[A]]](NilF())(ConsF(_, s.tail))) + def takeUpTo[N, T, A](implicit N: Recursive.Aux[N, Option], T: Recursive.Aux[T, ListF[A, ?]]): Coalgebra[ListF[A, ?], (N, T)] = + pair => pair._1.project.fold[ListF[A, (N, T)]](NilF())(p => pair._2.project.map((p, _))) + + def find[A](cond: A => Boolean): Algebra[ListF[A, ?], Option[A]] = { + case ConsF(h, t) => if (cond(h)) h.some else t + case NilF() => None + } + implicit def equal[A: Equal]: Delay[Equal, ListF[A, ?]] = new Delay[Equal, ListF[A, ?]] { def apply[β](eq: Equal[β]) = Equal.equal((a, b) => (a, b) match { diff --git a/project/build.properties b/project/build.properties index d91bf6c4..27e88aa1 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=0.13.13-RC2 +sbt.version=0.13.13 diff --git a/tests/src/test/scala/matryoshka/instances/fixedpoint/list.scala b/tests/src/test/scala/matryoshka/instances/fixedpoint/list.scala index 4c8a2612..d0da69f5 100644 --- a/tests/src/test/scala/matryoshka/instances/fixedpoint/list.scala +++ b/tests/src/test/scala/matryoshka/instances/fixedpoint/list.scala @@ -24,7 +24,7 @@ import scala.Int import org.specs2.ScalaCheck import org.specs2.mutable._ -import org.specs2.scalaz.{ScalazMatchers} +import org.specs2.scalaz.ScalazMatchers import scalaz._, Scalaz._ class ListSpec extends Specification with ScalaCheck with ScalazMatchers { @@ -37,14 +37,15 @@ class ListSpec extends Specification with ScalaCheck with ScalazMatchers { "fill" should { "be equivalent to scala.List.fill" >> prop { (v: Int) => - List.fill(100)(v).cata(ListF.listIso.get) must - equal(scala.List.fill(100)(v)) + 100.anaM[Nat](Nat.fromInt) ∘ (List.fill(_)(v).cata(ListF.listIso.get)) must + equal(scala.List.fill(100)(v).some) } } "length" should { "count the number of elements" >> prop { (v: Int) => - List.fill(30)(v).length must equal(30) + 30.anaM[Nat](Nat.fromInt) ∘ (List.fill(_)(v).length) must + equal(30.some) } } diff --git a/tests/src/test/scala/matryoshka/instances/fixedpoint/partial.scala b/tests/src/test/scala/matryoshka/instances/fixedpoint/partial.scala index c89cf752..8954c106 100644 --- a/tests/src/test/scala/matryoshka/instances/fixedpoint/partial.scala +++ b/tests/src/test/scala/matryoshka/instances/fixedpoint/partial.scala @@ -40,6 +40,9 @@ class PartialSpec extends Specification with ScalazMatchers with ScalaCheck with "satisfy relevant laws" in { checkAll(Props.equal.laws[Partial[Int]](Partial.equal, implicitly)) checkAll(Props.monad.laws[Partial](implicitly, implicitly, implicitly, implicitly, Partial.equal)) + // NB: We get Foldable for free due to `RecursiveT[Nu]` and + // `Bifoldable[\/]` + checkAll(Props.foldable.laws[Partial]) } } diff --git a/tests/src/test/scala/matryoshka/instances/fixedpoint/stream.scala b/tests/src/test/scala/matryoshka/instances/fixedpoint/stream.scala index 922758ee..10da1f1c 100644 --- a/tests/src/test/scala/matryoshka/instances/fixedpoint/stream.scala +++ b/tests/src/test/scala/matryoshka/instances/fixedpoint/stream.scala @@ -17,7 +17,6 @@ package matryoshka.instances.fixedpoint import matryoshka._ -import matryoshka.data.Nu import matryoshka.implicits._ import scala.Int @@ -31,7 +30,7 @@ class StreamSpec extends Specification with ScalaCheck with ScalazMatchers { /** Infinite sequence of Fibonacci numbers (at least until they overflow * int32) */ - val fib = (1, 0).ana[Nu[(Int, ?)]](binarySequence(_ + _)) + val fib = (1, 0).ana[Stream[Int]](binarySequence(_ + _)) /** Generates an infinite stream of the carrier value. */ @@ -43,32 +42,38 @@ class StreamSpec extends Specification with ScalaCheck with ScalazMatchers { } "lazily generate the correct sequence" in { - fib.drop(5).head must equal(8) + 5.anaM[Nat](Nat.fromInt) ∘ (fib.drop(_).head) must equal(8.some) } "have a proper prefix" in { - fib.take(5) must equal(List(1, 1, 2, 3, 5)) + 5.anaM[Nat](Nat.fromInt) ∘ fib.take[Nat, List[Int]] must + equal(List(1, 1, 2, 3, 5).some) } "get a subsequence" in { - fib.drop(10).take(5) must equal(List(89, 144, 233, 377, 610)) + (10.anaM[Nat](Nat.fromInt) ⊛ 5.anaM[Nat](Nat.fromInt))((d, t) => + fib.drop(d).take[Nat, List[Int]](t)) must + equal(List(89, 144, 233, 377, 610).some) } } "constantly" should { "begin with the given value" >> prop { (i: Int) => - i.ana[Nu[(Int, ?)]](constantly).head must_== i + i.ana[Stream[Int]](constantly).head must_== i } // FIXME: These two blow up the stack with much larger inputs "have the given value at an arbitrary point" >> prop { (i: Int, d: Int) => - i.ana[Nu[(Int, ?)]](constantly).drop(30000).head must equal(i) + 450.anaM[Nat](Nat.fromInt) ∘ + (i.ana[Stream[Int]](constantly).drop(_).head) must + equal(i.some) } "have subsequence of the given value" >> prop { (i: Int, t: Int) => - i.ana[Nu[(Int, ?)]](constantly).take(450) must - equal(List.fill(450)(i)) + 450.anaM[Nat](Nat.fromInt) ∘ + (i.ana[Stream[Int]](constantly).take[Nat, List[Int]](_)) must + equal(450.anaM[Nat](Nat.fromInt) ∘ (List.fill(_)(i))) } } } diff --git a/tests/src/test/scala/matryoshka/patterns/envt.scala b/tests/src/test/scala/matryoshka/patterns/envt.scala index 1578e41e..df020b38 100644 --- a/tests/src/test/scala/matryoshka/patterns/envt.scala +++ b/tests/src/test/scala/matryoshka/patterns/envt.scala @@ -17,6 +17,7 @@ package matryoshka.patterns import matryoshka._ +import matryoshka.data.cofree._ import matryoshka.exp._ import matryoshka.helpers._ import matryoshka.scalacheck.arbitrary._ @@ -37,6 +38,5 @@ class EnvTSpec extends Specification with CheckAll with AlgebraChecks { } } - // FIXME: enable this - // checkAlgebraIsoLaws("EnvT ⇔ Cofree", EnvT.cofreeIso[Int, Exp]) + checkAlgebraIsoLaws("EnvT ⇔ Cofree", EnvT.cofreeIso[Int, Exp]) } diff --git a/tests/src/test/scala/matryoshka/spec.scala b/tests/src/test/scala/matryoshka/spec.scala index d4a12f55..ce02dd85 100644 --- a/tests/src/test/scala/matryoshka/spec.scala +++ b/tests/src/test/scala/matryoshka/spec.scala @@ -1021,6 +1021,13 @@ class MatryoshkaSpecs extends Specification with ScalaCheck with ScalazMatchers } } + "quicksort" should { + "sort an arbitrary list" in { + quicksort(List(8, 29, 2002394, 9, 902098, 329123092, 202, 0, 2, 198)) must + equal(List(0, 2, 8, 9, 29, 198, 202, 902098, 2002394, 329123092)) + } + } + "find" should { val exp = mul(mul(num(10), mul(num(11), num(7))), mul(num(12), num(8)))