From 8b1684f5753b0f78337f6f1407090ae4ea2ac425 Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Fri, 11 Mar 2016 11:10:51 -0700 Subject: [PATCH 1/8] Fix and move implicits. --- CHANGELOG/minor.md | 1 + .../main/scala/matryoshka/AlgebraOps.scala | 53 +++- .../main/scala/matryoshka/CoalgebraOps.scala | 30 +- core/src/main/scala/matryoshka/Proj.scala | 2 +- core/src/main/scala/matryoshka/cofree.scala | 3 + core/src/main/scala/matryoshka/free.scala | 3 + core/src/main/scala/matryoshka/package.scala | 293 +++++++++++++----- core/src/test/scala/matryoshka/spec.scala | 21 +- 8 files changed, 322 insertions(+), 84 deletions(-) create mode 100644 CHANGELOG/minor.md diff --git a/CHANGELOG/minor.md b/CHANGELOG/minor.md new file mode 100644 index 00000000..8cee7a02 --- /dev/null +++ b/CHANGELOG/minor.md @@ -0,0 +1 @@ +- move `attributeCoelgotM` to `attributeElgotAlgebraM`, updating it to the generalized form. diff --git a/core/src/main/scala/matryoshka/AlgebraOps.scala b/core/src/main/scala/matryoshka/AlgebraOps.scala index 32b9754c..e0676ba9 100644 --- a/core/src/main/scala/matryoshka/AlgebraOps.scala +++ b/core/src/main/scala/matryoshka/AlgebraOps.scala @@ -16,13 +16,56 @@ package matryoshka -import scalaz._ -import scalaz.syntax.comonad._ +import scalaz._, Scalaz._ + +final class GAlgebraMOps[W[_], M[_], F[_], A](self: GAlgebraM[W, M, F, A]) { + def attribute(implicit W: Comonad[W], M: Functor[M], F: Functor[F]) = + matryoshka.attributeGAlgebraM[W, M, F, A](self) +} + +final class ElgotAlgebraMOps[W[_], M[_], F[_], A](self: ElgotAlgebraM[W, M, F, A]) { + def attribute(implicit W: Comonad[W], M: Functor[M], F: Functor[F]) = + matryoshka.attributeElgotAlgebraM[W, M, F, A](self) +} + +final class GAlgebraOps[W[_], F[_], A](self: GAlgebra[W, F, A]) { + def attribute(implicit W: Comonad[W], F: Functor[F]) = + matryoshka.attributeGAlgebraM[W, Id, F, A](self) + + def generalizeM[M[_]: Applicative]: GAlgebraM[W, M, F, A] = + matryoshka.generalizeM[M, F[W[A]], A](self) +} + +final class ElgotAlgebraOps[W[_], F[_], A](self: ElgotAlgebra[W, F, A]) { + def attribute(implicit W: Comonad[W], F: Functor[F]) = + matryoshka.attributeElgotAlgebraM[W, Id, F, A](self) + + def generalizeM[M[_]: Applicative]: ElgotAlgebraM[W, M, F, A] = + matryoshka.generalizeM[M, W[F[A]], A](self) +} + +final class AlgebraMOps[M[_], F[_], A](self: AlgebraM[M, F, A]) { + def attribute(implicit M: Functor[M], F: Functor[F]) = + matryoshka.attributeAlgebraM[M, F, A](self) + + def generalize[W[_]: Comonad](implicit F: Functor[F]): GAlgebraM[W, M, F, A] = + matryoshka.generalizeAlgebra[W, F, A, M[A]](self) + + def generalizeElgot[W[_]: Comonad]: ElgotAlgebraM[W, M, F, A] = + matryoshka.generalizeW[W, F[A], M[A]](self) +} + +final class AlgebraOps[F[_], A](self: Algebra[F, A]) { + def attribute(implicit F: Functor[F]) = + matryoshka.attributeAlgebra[F, A](self) -sealed class AlgebraOps[F[_], A](self: Algebra[F, A]) { def generalize[W[_]: Comonad](implicit F: Functor[F]): GAlgebra[W, F, A] = - node => self(node ∘ (_.copoint)) + matryoshka.generalizeAlgebra[W, F, A, A](self) def generalizeElgot[W[_]: Comonad]: ElgotAlgebra[W, F, A] = - w => self(w.copoint) + matryoshka.generalizeW[W, F[A], A](self) + + def generalizeM[M[_]: Applicative](implicit F: Functor[F]): + AlgebraM[M, F, A] = + matryoshka.generalizeM[M, F[A], A](self) } diff --git a/core/src/main/scala/matryoshka/CoalgebraOps.scala b/core/src/main/scala/matryoshka/CoalgebraOps.scala index 2b98528d..5578a87d 100644 --- a/core/src/main/scala/matryoshka/CoalgebraOps.scala +++ b/core/src/main/scala/matryoshka/CoalgebraOps.scala @@ -17,13 +17,33 @@ package matryoshka import scalaz._ -import scalaz.syntax.monad._ + +sealed class GCoalgebraOps[M[_], F[_], A](self: GCoalgebra[M, F, A]) { + def generalizeM[N[_]: Applicative]: + GCoalgebraM[M, N, F, A] = + matryoshka.generalizeM[N, A, F[M[A]]](self) +} + +sealed class ElgotCoalgebraOps[M[_], F[_], A](self: ElgotCoalgebra[M, F, A]) { + def generalizeM[N[_]: Applicative]: + ElgotCoalgebraM[M, N, F, A] = + matryoshka.generalizeM[N, A, M[F[A]]](self) +} + +sealed class CoalgebraMOps[M[_], F[_], A](self: CoalgebraM[M, F, A]) { + def generalize[N[_]: Applicative](implicit M: Functor[M], F: Functor[F]): + GCoalgebraM[N, M, F, A] = + matryoshka.generalizeCoalgebraM[M, N, F, A](self) +} sealed class CoalgebraOps[F[_], A](self: Coalgebra[F, A]) { - def generalize[M[_]: Monad](implicit F: Functor[F]): GCoalgebra[M, F, A] = - self(_).map(_.map(_.point[M])) + def generalize[M[_]: Applicative](implicit F: Functor[F]): + GCoalgebra[M, F, A] = + matryoshka.generalizeCoalgebra[M, F, A](self) - def generalizeM[M[_]: Monad]: CoalgebraM[M, F, A] = self(_).point[M] + def generalizeM[M[_]: Applicative]: CoalgebraM[M, F, A] = + matryoshka.generalizeM[M, A, F[A]](self) - def generalizeElgot[M[_]: Monad]: CoalgebraM[M, F, A] = self.generalizeM + def generalizeElgot[M[_]: Monad]: CoalgebraM[M, F, A] = + matryoshka.generalizeM[M, A, F[A]](self) } diff --git a/core/src/main/scala/matryoshka/Proj.scala b/core/src/main/scala/matryoshka/Proj.scala index ba1568ca..fc3830da 100644 --- a/core/src/main/scala/matryoshka/Proj.scala +++ b/core/src/main/scala/matryoshka/Proj.scala @@ -24,7 +24,7 @@ import scalaz._ /** An extractor to make it easier to pattern-match on arbitrary Recursive * structures. * - * NB: This extractor is irrufutable and doesn’t break exhaustiveness checking. + * NB: This extractor is irrefutable and doesn’t break exhaustiveness checking. */ object Proj { def unapply[T[_[_]]: Recursive, F[_]: Functor](obj: T[F]): Some[F[T[F]]] = diff --git a/core/src/main/scala/matryoshka/cofree.scala b/core/src/main/scala/matryoshka/cofree.scala index 5d9da041..3a37f21f 100644 --- a/core/src/main/scala/matryoshka/cofree.scala +++ b/core/src/main/scala/matryoshka/cofree.scala @@ -40,6 +40,9 @@ trait CofreeInstances { implicit def cofreeShow[F[_], A: Show](implicit F: (Show ~> λ[α => Show[F[α]]])): Show[Cofree[F, A]] = Show.shows(cof => "(" + cof.head.show + ", " + F(cofreeShow).shows(cof.tail) + ")") + + implicit def toCofreeOps[F[_], A](a: Cofree[F, A]): CofreeOps[F, A] = + new CofreeOps[F, A](a) } object cofree extends CofreeInstances diff --git a/core/src/main/scala/matryoshka/free.scala b/core/src/main/scala/matryoshka/free.scala index 9a2cfe26..c0cd4f8d 100644 --- a/core/src/main/scala/matryoshka/free.scala +++ b/core/src/main/scala/matryoshka/free.scala @@ -31,6 +31,9 @@ trait FreeInstances { _.point[Free[G, ?]].point[M], f(_).map(Free.liftF(_).join)) } + + implicit def toFreeOps[F[_], A](a: Free[F, A]): FreeOps[F, A] = + new FreeOps[F, A](a) } object free extends FreeInstances diff --git a/core/src/main/scala/matryoshka/package.scala b/core/src/main/scala/matryoshka/package.scala index 59f5c3d2..3979ebee 100644 --- a/core/src/main/scala/matryoshka/package.scala +++ b/core/src/main/scala/matryoshka/package.scala @@ -22,7 +22,24 @@ import scala.collection.immutable.{List, ::} import scalaz._, Scalaz._ import simulacrum.typeclass -/** Generalized folds, unfolds, and refolds. */ +/** Generalized folds, unfolds, and refolds. + * + * @groupname Algebra [Co]Algebras + * @groupname Fold Folds + * @groupdesc Fold + * These are folds that don’t fit under [[matryoshka.Recursive]] + * because they fold from a specific fixed point operator. + * @groupname Unfold Unfolds + * @groupdesc Unfold + * These are unfolds that don’t fit under [[matryoshka.Corecursive]] + * because they unfold to a specific fixed point operator. + * @groupname Refold Refolds + * @groupdesc Refold + * These operations apply a function both on the way to the leaves + * of the structure as well as on the way back, so they avoid ever + * constructing an intermediate fixed point structure. + * @groupname Dist DistributiveLaws + */ package object matryoshka extends CofreeInstances with FreeInstances { def lambek[T[_[_]]: Corecursive: Recursive, F[_]: Functor](tf: T[F]): @@ -33,34 +50,127 @@ package object matryoshka extends CofreeInstances with FreeInstances { T[F] = ft.ana(_ ∘ (_.project)) - type GAlgebraM[W[_], M[_], F[_], A] = F[W[A]] => M[A] + type GAlgebraM[W[_], M[_], F[_], A] = F[W[A]] => M[A] + object GAlgebraM { + implicit def toOps[W[_], M[_], F[_], A](a: GAlgebraM[W, M, F, A]): + GAlgebraMOps[W, M, F, A] = + new GAlgebraMOps[W, M, F, A](a) + } + type GAlgebra[W[_], F[_], A] = GAlgebraM[W, Id, F, A] // F[W[A]] => A - type AlgebraM[M[_], F[_], A] = GAlgebraM[Id, M, F, A] // F[A] => M[A] - type Algebra[F[_], A] = GAlgebra[Id, F, A] // F[A] => A + object GAlgebra { + implicit def zip[W[_]: Functor, F[_]: Functor]: Zip[GAlgebra[W, F, ?]] = + new Zip[GAlgebra[W, F, ?]] { + def zip[A, B](a: ⇒ GAlgebra[W, F, A], b: ⇒ GAlgebra[W, F, B]) = + node => (a(node ∘ (_ ∘ (_._1))), b(node ∘ (_ ∘ (_._2)))) + } + + implicit def toOps[W[_], F[_], A](a: GAlgebra[W, F, A]): GAlgebraOps[W, F, A] = + new GAlgebraOps[W, F, A](a) + } + + type AlgebraM[M[_], F[_], A] = GAlgebraM[Id, M, F, A] // F[A] => M[A] + object AlgebraM { + implicit def toOps[M[_], F[_], A](a: AlgebraM[M, F, A]): + AlgebraMOps[M, F, A] = + new AlgebraMOps[M, F, A](a) + } + + type Algebra[F[_], A] = GAlgebra[Id, F, A] // F[A] => A + object Algebra { + implicit def zip[F[_]: Functor] = GAlgebra.zip[Id, F] + + implicit def toOps[F[_], A](a: Algebra[F, A]): AlgebraOps[F, A] = + new AlgebraOps[F, A](a) + } + type ElgotAlgebraM[W[_], M[_], F[_], A] = W[F[A]] => M[A] + object ElgotAlgebraM { + implicit def zip[W[_]: Functor, M[_]: Applicative, F[_]: Functor]: + Zip[ElgotAlgebraM[W, M, F, ?]] = + new Zip[ElgotAlgebraM[W, M, F, ?]] { + def zip[A, B](a: ⇒ ElgotAlgebraM[W, M, F, A], b: ⇒ ElgotAlgebraM[W, M, F, B]) = + w => Bitraverse[(?, ?)].bisequence((a(w ∘ (_ ∘ (_._1))), b(w ∘ (_ ∘ (_._2))))) + } + + implicit def toOps[W[_], M[_], F[_], A](a: ElgotAlgebraM[W, M, F, A]): + ElgotAlgebraMOps[W, M, F, A] = + new ElgotAlgebraMOps[W, M, F, A](a) + } + type ElgotAlgebra[W[_], F[_], A] = ElgotAlgebraM[W, Id, F, A] // W[F[A]] => A + object ElgotAlgebra { + implicit def zip[W[_]: Functor, F[_]: Functor] = ElgotAlgebraM.zip[W, Id, F] + // (ann, node) => node.unfzip.bimap(f(ann, _), g(ann, _)) + + implicit def toOps[W[_], F[_], A](a: ElgotAlgebra[W, F, A]): + ElgotAlgebraOps[W, F, A] = + new ElgotAlgebraOps[W, F, A](a) + } - type GCoalgebraM[N[_], M[_], F[_], A] = A => M[F[N[A]]] - type GCoalgebra[N[_], F[_], A] = GCoalgebraM[N, Id, F, A] // A => N[F[A]] - type CoalgebraM[M[_], F[_], A] = GCoalgebraM[Id, M, F, A] // A => F[M[A]] - type Coalgebra[F[_], A] = GCoalgebra[Id, F, A] // A => F[A] + /** A coalgebra with two monads, one handled by the fold and the other in the + * result. `N` is the fold’s monad, whereas `M` is the Kleisli. + */ + type GCoalgebraM[N[_], M[_], F[_], A] = A => M[F[N[A]]] + + type GCoalgebra[N[_], F[_], A] = GCoalgebraM[N, Id, F, A] // A => F[N[A]] + object GCoalgebra { + implicit def toOps[M[_], F[_], A](a: GCoalgebra[M, F, A]): + GCoalgebraOps[M, F, A] = + new GCoalgebraOps[M, F, A](a) + } + + type CoalgebraM[M[_], F[_], A] = GCoalgebraM[Id, M, F, A] // A => M[F[A]] + object CoalgebraM { + implicit def toOps[M[_], F[_], A](a: CoalgebraM[M, F, A]): + CoalgebraMOps[M, F, A] = + new CoalgebraMOps[M, F, A](a) + } + + type Coalgebra[F[_], A] = GCoalgebra[Id, F, A] // A => F[A] + object Coalgebra { + implicit def toOps[F[_], A](a: Coalgebra[F, A]): CoalgebraOps[F, A] = + new CoalgebraOps[F, A](a) + } + type ElgotCoalgebraM[N[_], M[_], F[_], A] = A => M[N[F[A]]] - /** A NaturalTransformation that sequences two types */ + /** Identical to [[matryoshka.CoalgebraM]] in structure, this signifies a + * different intent – the dual of [[matryoshka.ElgotAlgebra]]. + */ + type ElgotCoalgebra[N[_], F[_], A] = ElgotCoalgebraM[N, Id, F, A] // A => N[F[A]] + object ElgotCoalgebra { + implicit def toOps[M[_], F[_], A](a: ElgotCoalgebra[M, F, A]): + ElgotCoalgebraOps[M, F, A] = + new ElgotCoalgebraOps[M, F, A](a) + } + + /** A `NaturalTransformation` that sequences two types. + * + * @group Dist + */ type DistributiveLaw[F[_], G[_]] = λ[α => F[G[α]]] ~> λ[α => G[F[α]]] /** This folds a Free that you may think of as “already partially-folded”. * It’s also the fold of a decomposed `elgot`. + * + * @group Fold */ def interpretCata[F[_]: Functor, A](t: Free[F, A])(φ: F[A] => A): A = t.fold(x => x, f => φ(f ∘ (interpretCata(_)(φ)))) /** The fold of a decomposed `coelgot`, since the Cofree already has the * attribute for each node. + * + * @group Fold */ def elgotCata[F[_]: Functor, A, B](t: Cofree[F, A])(φ: ((A, F[B])) => B): B = φ((t.head, t.tail ∘ (elgotCata(_)(φ)))) + /** A Kleisli version of [[matryoshka.elgotCata]]. + * + * @group Fold + */ def elgotCataM[F[_]: Traverse, M[_]: Monad, A, B]( t: Cofree[F, A])( φ: ((A, F[B])) => M[B]): @@ -70,42 +180,70 @@ package object matryoshka extends CofreeInstances with FreeInstances { /** A version of [[matryoshka.Corecursive.ana]] that annotates each node with * the `A` it was expanded from. This is also the unfold from a decomposed * `coelgot`. + * + * @group Unfold */ def attributeAna[F[_]: Functor, A](a: A)(ψ: A => F[A]): Cofree[F, A] = Cofree(a, ψ(a) ∘ (attributeAna(_)(ψ))) - /** A Kleisli [[matryoshka.attributeAna]]. */ - def attributeAnaM[M[_]: Monad, F[_]: Traverse, A](a: A)(ψ: A => M[F[A]]): M[Cofree[F, A]] = + /** A Kleisli [[matryoshka.attributeAna]]. + * + * @group Unfold + */ + def attributeAnaM[M[_]: Monad, F[_]: Traverse, A](a: A)(ψ: A => M[F[A]]): + M[Cofree[F, A]] = ψ(a).flatMap(_.traverse(attributeAnaM(_)(ψ))) ∘ (Cofree(a, _)) - /** The unfold from a decomposed `elgot`. */ + /** The unfold from a decomposed `elgot`. + * + * @group Unfold + */ def elgotAna[F[_]: Functor, A, B](a: A)(ψ: A => B \/ F[A]): Free[F, B] = ψ(a).fold( _.point[Free[F, ?]], fb => Free.liftF(fb ∘ (elgotAna(_)(ψ))).join) - def cofCataM[S[_]: Traverse, M[_]: Monad, A, B](t: Cofree[S, A])(f: (A, S[B]) => M[B]): M[B] = + /** The “second” fold from `elgotZygo`, with the “helper value” coming from + * `Cofree` rather than from another algebra. + * + * @group Fold + */ + def cofCataM[S[_]: Traverse, M[_]: Monad, A, B](t: Cofree[S, A])(f: (A, S[B]) => M[B]): + M[B] = t.tail.traverse(cofCataM(_)(f)) >>= (f(t.head, _)) + /** This is the “second” fold from `paraElgotZygo`, with the comonad + * structures coming from `Cofree` rather than from another algebra. + * + * @group Fold + */ def cofParaM[M[_]] = new CofParaMPartiallyApplied[M] 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.traverseU(cs => self(cs)(f) ∘ ((Recursive[Cofree[?[_], A]].convertTo[S, T](cs), _))) >>= (f(t.head, _)) } - /** Composition of an anamorphism and a catamorphism that avoids building the - * intermediate recursive data structure. + /** A hylomorphism is a composition of [[matryoshka.Corecursive.ana]] and + * [[matryoshka.Recursive.cata]] that avoids building the intermediate + * recursive data structure. + * + * @group Refold */ def hylo[F[_]: Functor, A, B](a: A)(f: F[B] => B, g: A => F[A]): B = f(g(a) ∘ (hylo(_)(f, g))) - /** A Kleisli hylomorphism. */ + /** A Kleisli [[matryoshka.hylo]]. + * + * @group Refold + */ def hyloM[M[_]: Monad, F[_]: Traverse, A, B](a: A)(f: F[B] => M[B], g: A => M[F[A]]): M[B] = g(a) >>= (_.traverse(hyloM(_)(f, g)) >>= (f)) - /** A generalized version of a hylomorphism that composes any coalgebra and - * algebra. + /** A generalized version of [[matryoshka.hylo]] that composes any coalgebra + * and algebra. + * + * @group Refold */ def ghylo[F[_]: Functor, W[_]: Comonad, M[_], A, B]( a: A)( @@ -119,8 +257,10 @@ package object matryoshka extends CofreeInstances with FreeInstances { h(a.point[M]).copoint } - /** Similar to a hylomorphism, this composes a futumorphism and a - * histomorphism. + /** Similar to [[matryoshka.hylo]], this composes + * [[matryoshka.Corecursive.futu]] and [[matryoshka.Recursive.histo]]. + * + * @group Refold */ def chrono[F[_]: Functor, A, B]( a: A)( @@ -128,12 +268,22 @@ package object matryoshka extends CofreeInstances with FreeInstances { B = ghylo[F, Cofree[F, ?], Free[F, ?], A, B](a)(distHisto, distFutu, g, f) + /** Similar to [[matryoshka.hylo]], `elgot` allows the unfold to short-circuit + * by returning the final value for a branch directly. + * + * @group Refold + */ def elgot[F[_]: Functor, A, B](a: A)(φ: F[B] => B, ψ: A => B \/ F[A]): B = { def h: A => B = (((x: B) => x) ||| ((x: F[A]) => φ(x ∘ h))) ⋘ ψ h(a) } + /** The dual of [[matryoshka.elgot]], `coelgot` has access to the pre-unfolded + * value for each node. + * + * @group Refold + */ def coelgot[F[_]: Functor, A, B](a: A)(φ: ((A, F[B])) => B, ψ: A => F[A]): B = { def h: A => B = @@ -141,6 +291,10 @@ package object matryoshka extends CofreeInstances with FreeInstances { h(a) } + /** A Kleisli version of [[matryoshka.coelgot]]. + * + * @group Refold + */ def coelgotM[M[_]] = new CoelgotMPartiallyApplied[M] final class CoelgotMPartiallyApplied[M[_]] { def apply[F[_]: Traverse, A, B](a: A)(φ: ((A, F[B])) => M[B], ψ: A => M[F[A]])(implicit M: Monad[M]): @@ -150,23 +304,28 @@ package object matryoshka extends CofreeInstances with FreeInstances { } } + /** @group Dist */ def distPara[T[_[_]], F[_]: Functor](implicit T: Corecursive[T]): DistributiveLaw[F, (T[F], ?)] = distZygo(_.embed) + /** @group Dist */ def distParaT[T[_[_]], F[_]: Functor, W[_]: Comonad]( t: DistributiveLaw[F, W])( implicit T: Corecursive[T]): DistributiveLaw[F, EnvT[T[F], W, ?]] = distZygoT(_.embed, t) + /** @group Dist */ def distCata[F[_]]: DistributiveLaw[F, Id] = NaturalTransformation.refl + /** @group Dist */ def distZygo[F[_]: Functor, B](g: F[B] => B) = new DistributiveLaw[F, (B, ?)] { def apply[α](m: F[(B, α)]) = (g(m ∘ (_._1)), m ∘ (_._2)) } + /** @group Dist */ def distZygoT[F[_], W[_]: Comonad, B]( g: F[B] => B, k: DistributiveLaw[F, W])( implicit F: Functor[F]) = @@ -177,12 +336,14 @@ package object matryoshka extends CofreeInstances with FreeInstances { k(F.lift[EnvT[B, W, α], W[α]](_.lower)(fe)))) } + /** @group Dist */ def distHisto[F[_]: Functor] = new DistributiveLaw[F, Cofree[F, ?]] { def apply[α](m: F[Cofree[F, α]]) = distGHisto[F, F](NaturalTransformation.refl[λ[α => F[F[α]]]]).apply(m) } + /** @group Dist */ def distGHisto[F[_], H[_]]( k: DistributiveLaw[F, H])( implicit F: Functor[F], H: Functor[H]) = @@ -193,14 +354,17 @@ package object matryoshka extends CofreeInstances with FreeInstances { k(F.lift[Cofree[H, α], H[Cofree[H, α]]](_.tail)(as)))) } + /** @group Dist */ def distAna[F[_]]: DistributiveLaw[Id, F] = NaturalTransformation.refl + /** @group Dist */ def distFutu[F[_]: Functor] = new DistributiveLaw[Free[F, ?], F] { def apply[α](m: Free[F, F[α]]) = distGFutu[F, F](NaturalTransformation.refl[λ[α => F[F[α]]]]).apply(m) } + /** @group Dist */ def distGFutu[H[_], F[_]]( k: DistributiveLaw[H, F])( implicit H: Functor[H], F: Functor[F]): DistributiveLaw[Free[H, ?], F] = @@ -242,16 +406,17 @@ package object matryoshka extends CofreeInstances with FreeInstances { /** Turns any F-algebra, into an identical one that attributes the tree with * the results for each node. */ - def attributeM[F[_]: Functor, M[_]: Functor, A](f: F[A] => M[A]): - F[Cofree[F, A]] => M[Cofree[F, A]] = + def attributeAlgebraM[M[_]: Functor, F[_]: Functor, A](f: AlgebraM[M, F, A]): + AlgebraM[M, F, Cofree[F, A]] = fa => f(fa ∘ (_.head)) ∘ (Cofree(_, fa)) - def attribute[F[_]: Functor, A](f: F[A] => A) = attributeM[F, Id, A](f) + def attributeAlgebra[F[_]: Functor, A](f: Algebra[F, A]) = + attributeAlgebraM[Id, F, A](f) - def attrK[F[_]: Functor, A](k: A) = attribute[F, A](Function.const(k)) + def attrK[F[_]: Functor, A](k: A) = attributeAlgebra[F, A](Function.const(k)) def attrSelf[T[_[_]]: Corecursive, F[_]: Functor] = - attribute[F, T[F]](_.embed) + attributeAlgebra[F, T[F]](_.embed) /** NB: Since Cofree carries the functor, the resulting algebra is a cata, not * a para. */ @@ -259,30 +424,28 @@ package object matryoshka extends CofreeInstances with FreeInstances { F[Cofree[F, A]] => Cofree[F, A] = fa => Cofree(f(fa ∘ (x => (Recursive[Cofree[?[_], A]].convertTo[F, T](x), x.head))), fa) - def attributeCoelgotM[M[_]] = new AttributeCoelgotMPartiallyApplied[M] - final class AttributeCoelgotMPartiallyApplied[M[_]] { - def apply[F[_]: Functor, A, B](f: (A, F[B]) => M[B])(implicit M: Functor[M]): - (A, F[Cofree[F, B]]) => M[Cofree[F, B]] = - (a, node) => f(a, node ∘ (_.head)) ∘ (Cofree(_, node)) - } - - implicit def GAlgebraZip[W[_]: Functor, F[_]: Functor]: - Zip[GAlgebra[W, F, ?]] = - new Zip[GAlgebra[W, F, ?]] { - def zip[A, B](a: ⇒ GAlgebra[W, F, A], b: ⇒ GAlgebra[W, F, B]) = - node => (a(node ∘ (_ ∘ (_._1))), b(node ∘ (_ ∘ (_._2)))) - } - implicit def AlgebraZip[F[_]: Functor] = GAlgebraZip[Id, F] - - implicit def ElgotAlgebraMZip[W[_]: Functor, M[_]: Applicative, F[_]: Functor]: - Zip[ElgotAlgebraM[W, M, F, ?]] = - new Zip[ElgotAlgebraM[W, M, F, ?]] { - def zip[A, B](a: ⇒ ElgotAlgebraM[W, M, F, A], b: ⇒ ElgotAlgebraM[W, M, F, B]) = - w => Bitraverse[(?, ?)].bisequence((a(w ∘ (_ ∘ (_._1))), b(w ∘ (_ ∘ (_._2))))) - } - implicit def ElgotAlgebraZip[W[_]: Functor, F[_]: Functor] = - ElgotAlgebraMZip[W, Id, F] - // (ann, node) => node.unfzip.bimap(f(ann, _), g(ann, _)) + def attributeGAlgebraM[W[_]: Comonad, M[_]: Functor, F[_]: Functor, A]( + f: GAlgebraM[W, M, F, A]): + GAlgebraM[W, M, F, Cofree[F, A]] = + node => f(node ∘ (_ ∘ (_.head))) ∘ (Cofree(_, node ∘ (_.copoint))) + + def attributeElgotAlgebraM[W[_]: Comonad, M[_]: Functor, F[_]: Functor, A]( + f: ElgotAlgebraM[W, M, F, A]): + ElgotAlgebraM[W, M, F, Cofree[F, A]] = + node => f(node ∘ (_ ∘ (_.head))) ∘ (Cofree(_, node.copoint)) + + def generalizeM[M[_]: Applicative, A, B](f: A => B): A => M[B] = f(_).point[M] + def generalizeW[W[_]: Comonad, A, B](f: A => B): W[A] => B = w => f(w.copoint) + def generalizeAlgebra[W[_]: Comonad, F[_]: Functor, A, B](f: F[A] => B): + F[W[A]] => B = + node => f(node ∘ (_.copoint)) + def generalizeCoalgebra[M[_]: Applicative, F[_]: Functor, A](f: A => F[A]): + A => F[M[A]] = + f(_).map(_.point[M]) + def generalizeCoalgebraM[M[_]: Functor, N[_]: Applicative, F[_]: Functor, A]( + f: A => M[F[A]]): + A => M[F[N[A]]] = + f(_).map(_.map(_.point[N])) /** Repeatedly applies the function to the result as long as it returns Some. * Finally returns the last non-None value (which may be the initial input). @@ -290,20 +453,24 @@ package object matryoshka extends CofreeInstances with FreeInstances { def repeatedly[A](f: A => Option[A]): A => A = expr => f(expr).fold(expr)(repeatedly(f)) - /** Converts a failable fold into a non-failable, by simply returning the - * argument upon failure. - */ - def orOriginal[A](f: A => Option[A]): A => A = expr => f(expr).getOrElse(expr) - /** Converts a failable fold into a non-failable, by returning the default - * upon failure. + * upon failure. */ def orDefault[A, B](default: B)(f: A => Option[B]): A => B = - expr => f(expr).getOrElse(default) + f(_).getOrElse(default) + + /** Converts a failable fold into a non-failable, by simply returning the + * argument upon failure. + */ + def orOriginal[A](f: A => Option[A]): A => A = + expr => orDefault(expr)(f)(expr) /** Count the instinces of `form` in the structure. + * + * @group Algebra */ - def count[T[_[_]]: Recursive, F[_]: Functor: Foldable](form: T[F]): F[(T[F], Int)] => Int = + def count[T[_[_]]: Recursive, F[_]: Functor: Foldable](form: T[F]): + F[(T[F], Int)] => Int = e => e.foldRight(if (e ∘ (_._1) == form.project) 1 else 0)(_._2 + _) implicit def ToIdOps[A](a: A): IdOps[A] = new IdOps[A](a) @@ -311,16 +478,4 @@ package object matryoshka extends CofreeInstances with FreeInstances { implicit def ToCorecursiveOps[T[_[_]]: Corecursive, F[_]](f: F[T[F]]): CorecursiveOps[T, F] = new CorecursiveOps[T, F](f) - - implicit def ToAlgebraOps[F[_], A](a: Algebra[F, A]): AlgebraOps[F, A] = - new AlgebraOps[F, A](a) - - implicit def ToCoalgebraOps[F[_], A](a: Coalgebra[F, A]): CoalgebraOps[F, A] = - new CoalgebraOps[F, A](a) - - implicit def ToCofreeOps[F[_], A](a: Cofree[F, A]): CofreeOps[F, A] = - new CofreeOps[F, A](a) - - implicit def ToFreeOps[F[_], A](a: Free[F, A]): FreeOps[F, A] = - new FreeOps[F, A](a) } diff --git a/core/src/test/scala/matryoshka/spec.scala b/core/src/test/scala/matryoshka/spec.scala index 6406caec..f67cde87 100644 --- a/core/src/test/scala/matryoshka/spec.scala +++ b/core/src/test/scala/matryoshka/spec.scala @@ -99,8 +99,8 @@ object Exp { // NB: Something like this currently needs to be defined for any Functor in // order to get the generalize operations for the algebra. - implicit def ToExpAlgebraOps[A](a: Algebra[Exp, A]): AlgebraOps[Exp, A] = - ToAlgebraOps[Exp, A](a) + implicit def toOps[A](a: Algebra[Exp, A]): AlgebraOps[Exp, A] = + Algebra.toOps[Exp, A](a) implicit val ExpShow: Show ~> λ[α => Show[Exp[α]]] = new (Show ~> λ[α => Show[Exp[α]]]) { @@ -423,11 +423,24 @@ class FixplateSpecs extends Specification with ScalaCheck with ScalazMatchers { } } + "attribute" should { + "work on simple algebra" in { + val v = mul(num(1), mul(num(2), num(3))) + val rez = Cofree[Exp, Int](6, Mul( + Cofree(1, Num(1)), + Cofree (6, Mul( + Cofree(2, Num(2)), + Cofree(3, Num(3)))))) + v.cata(eval.attribute) must equal(rez) + v.convertTo[Mu].cata(eval.attribute) must equal(rez) + } + } + "zipAlgebras" should { "both eval and find all constants" in { - mul(num(5), num(2)).cata(AlgebraZip[Exp].zip(eval, findConstants)) must + mul(num(5), num(2)).cata(Algebra.zip[Exp].zip(eval, findConstants)) must equal((10, List(5, 2))) - mul(num(5), num(2)).convertTo[Mu].cata(AlgebraZip[Exp].zip(eval, findConstants)) must + mul(num(5), num(2)).convertTo[Mu].cata(Algebra.zip[Exp].zip(eval, findConstants)) must equal((10, List(5, 2))) } } From e2055379f5bca7dd1c268781bd9dc073d8e45e89 Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Fri, 11 Mar 2016 16:51:01 -0700 Subject: [PATCH 2/8] Add transApoM. --- core/src/main/scala/matryoshka/TraverseT.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/scala/matryoshka/TraverseT.scala b/core/src/main/scala/matryoshka/TraverseT.scala index fa8ebda0..c8ccaa46 100644 --- a/core/src/main/scala/matryoshka/TraverseT.scala +++ b/core/src/main/scala/matryoshka/TraverseT.scala @@ -40,6 +40,10 @@ import simulacrum.typeclass def transAnaM[M[_]: Monad, F[_]: Functor, G[_]: Traverse](t: T[F])(f: F[T[F]] => M[G[T[F]]]): M[T[G]] = traverse(t)(f(_).flatMap(_.traverse(transAnaM(_)(f)))) + def transApoM[M[_]: Monad, F[_]: Functor, G[_]: Traverse](t: T[F])(f: F[T[F]] => M[G[T[G] \/ T[F]]]): + M[T[G]] = + traverse(t)(f(_).flatMap(_.traverse(_.fold(_.point[M], transApoM(_)(f))))) + def topDownCataM[F[_]: Traverse, M[_]: Monad, A]( t: T[F], a: A)( f: (A, T[F]) => M[(A, T[F])]): From 889c6522b38184bfca225f2bdafd6771a317d305 Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Fri, 11 Mar 2016 23:10:47 -0700 Subject: [PATCH 3/8] Use classes to fix some issues with algebra types. --- .../main/scala/matryoshka/AlgebraOps.scala | 59 ++------ .../main/scala/matryoshka/CoalgebraOps.scala | 36 ++--- core/src/main/scala/matryoshka/IdOps.scala | 4 +- core/src/main/scala/matryoshka/algebra.scala | 115 ++++++++++++++ core/src/main/scala/matryoshka/package.scala | 143 ++++-------------- core/src/test/scala/matryoshka/spec.scala | 15 +- 6 files changed, 177 insertions(+), 195 deletions(-) create mode 100644 core/src/main/scala/matryoshka/algebra.scala diff --git a/core/src/main/scala/matryoshka/AlgebraOps.scala b/core/src/main/scala/matryoshka/AlgebraOps.scala index e0676ba9..bf06ab6f 100644 --- a/core/src/main/scala/matryoshka/AlgebraOps.scala +++ b/core/src/main/scala/matryoshka/AlgebraOps.scala @@ -18,54 +18,25 @@ package matryoshka import scalaz._, Scalaz._ -final class GAlgebraMOps[W[_], M[_], F[_], A](self: GAlgebraM[W, M, F, A]) { - def attribute(implicit W: Comonad[W], M: Functor[M], F: Functor[F]) = - matryoshka.attributeGAlgebraM[W, M, F, A](self) -} +final class GElgotAlgebraMOps[E[_], G[_], M[_], F[_], A]( + self: GElgotAlgebraM[E, G, M, F, A]) { -final class ElgotAlgebraMOps[W[_], M[_], F[_], A](self: ElgotAlgebraM[W, M, F, A]) { - def attribute(implicit W: Comonad[W], M: Functor[M], F: Functor[F]) = - matryoshka.attributeElgotAlgebraM[W, M, F, A](self) + def attribute(implicit E: Comonad[E], G: Comonad[G], M: Functor[M], F: Functor[F]) = + matryoshka.attribute[E, G, M, F, A](self) } - -final class GAlgebraOps[W[_], F[_], A](self: GAlgebra[W, F, A]) { - def attribute(implicit W: Comonad[W], F: Functor[F]) = - matryoshka.attributeGAlgebraM[W, Id, F, A](self) - - def generalizeM[M[_]: Applicative]: GAlgebraM[W, M, F, A] = - matryoshka.generalizeM[M, F[W[A]], A](self) + +final class GAlgebraMOps[G[_], M[_], F[_], A](self: GAlgebraM[G, M, F, A]) { + def generalizeElgot[E[_]: Comonad]: GElgotAlgebraM[E, G, M, F, A] = + matryoshka.generalizeW[E, F[G[A]], M[A]](self) } -final class ElgotAlgebraOps[W[_], F[_], A](self: ElgotAlgebra[W, F, A]) { - def attribute(implicit W: Comonad[W], F: Functor[F]) = - matryoshka.attributeElgotAlgebraM[W, Id, F, A](self) - - def generalizeM[M[_]: Applicative]: ElgotAlgebraM[W, M, F, A] = - matryoshka.generalizeM[M, W[F[A]], A](self) +final class ElgotAlgebraMOps[E[_], M[_], F[_], A](self: ElgotAlgebraM[E, M, F, A]) { + def generalize[G[_]: Comonad](implicit EF: Functor[λ[α => E[F[α]]]]): + GElgotAlgebraM[E, G, M, F, A] = + matryoshka.generalizeAlgebra[λ[α => E[F[α]]], G, Id, M, F, A](self) } -final class AlgebraMOps[M[_], F[_], A](self: AlgebraM[M, F, A]) { - def attribute(implicit M: Functor[M], F: Functor[F]) = - matryoshka.attributeAlgebraM[M, F, A](self) - - def generalize[W[_]: Comonad](implicit F: Functor[F]): GAlgebraM[W, M, F, A] = - matryoshka.generalizeAlgebra[W, F, A, M[A]](self) - - def generalizeElgot[W[_]: Comonad]: ElgotAlgebraM[W, M, F, A] = - matryoshka.generalizeW[W, F[A], M[A]](self) -} - -final class AlgebraOps[F[_], A](self: Algebra[F, A]) { - def attribute(implicit F: Functor[F]) = - matryoshka.attributeAlgebra[F, A](self) - - def generalize[W[_]: Comonad](implicit F: Functor[F]): GAlgebra[W, F, A] = - matryoshka.generalizeAlgebra[W, F, A, A](self) - - def generalizeElgot[W[_]: Comonad]: ElgotAlgebra[W, F, A] = - matryoshka.generalizeW[W, F[A], A](self) - - def generalizeM[M[_]: Applicative](implicit F: Functor[F]): - AlgebraM[M, F, A] = - matryoshka.generalizeM[M, F[A], A](self) +final class GElgotAlgebraOps[E[_], G[_], F[_], A](self: GElgotAlgebra[E, G, F, A]) { + def generalizeM[M[_]: Applicative]: GElgotAlgebraM[E, G, M, F, A] = + matryoshka.generalizeM[M, E[F[G[A]]], A](self) } diff --git a/core/src/main/scala/matryoshka/CoalgebraOps.scala b/core/src/main/scala/matryoshka/CoalgebraOps.scala index 5578a87d..f9a59e50 100644 --- a/core/src/main/scala/matryoshka/CoalgebraOps.scala +++ b/core/src/main/scala/matryoshka/CoalgebraOps.scala @@ -16,34 +16,20 @@ package matryoshka -import scalaz._ +import scalaz._, Scalaz._ -sealed class GCoalgebraOps[M[_], F[_], A](self: GCoalgebra[M, F, A]) { - def generalizeM[N[_]: Applicative]: - GCoalgebraM[M, N, F, A] = - matryoshka.generalizeM[N, A, F[M[A]]](self) +final class GElgotCoalgebraOps[E[_], G[_], F[_], A](self: GElgotCoalgebra[E, G, F, A]) { + def generalizeM[M[_]: Applicative]: + GElgotCoalgebraM[E, G, M, F, A] = + matryoshka.generalizeM[M, A, E[F[G[A]]]](self) } -sealed class ElgotCoalgebraOps[M[_], F[_], A](self: ElgotCoalgebra[M, F, A]) { - def generalizeM[N[_]: Applicative]: - ElgotCoalgebraM[M, N, F, A] = - matryoshka.generalizeM[N, A, M[F[A]]](self) +final class ElgotCoalgebraMOps[E[_], M[_], F[_], A](self: ElgotCoalgebraM[E, M, F, A]) { + def generalize[G[_]: Applicative](implicit MEF: Functor[λ[α => M[E[F[α]]]]]): GElgotCoalgebraM[E, G, M, F, A] = + matryoshka.generalizeCoalgebra[λ[α => M[E[F[α]]]], G, Id, A](self) } -sealed class CoalgebraMOps[M[_], F[_], A](self: CoalgebraM[M, F, A]) { - def generalize[N[_]: Applicative](implicit M: Functor[M], F: Functor[F]): - GCoalgebraM[N, M, F, A] = - matryoshka.generalizeCoalgebraM[M, N, F, A](self) -} - -sealed class CoalgebraOps[F[_], A](self: Coalgebra[F, A]) { - def generalize[M[_]: Applicative](implicit F: Functor[F]): - GCoalgebra[M, F, A] = - matryoshka.generalizeCoalgebra[M, F, A](self) - - def generalizeM[M[_]: Applicative]: CoalgebraM[M, F, A] = - matryoshka.generalizeM[M, A, F[A]](self) - - def generalizeElgot[M[_]: Monad]: CoalgebraM[M, F, A] = - matryoshka.generalizeM[M, A, F[A]](self) +final class GCoalgebraMOps[G[_], M[_], F[_], A](self: GCoalgebraM[G, M, F, A]) { + def generalizeElgot[E[_]: Applicative](implicit M: Functor[M]): GElgotCoalgebraM[E, G, M, F, A] = + matryoshka.generalizeCoalgebra[M, E, λ[α => F[G[α]]], A](self) } diff --git a/core/src/main/scala/matryoshka/IdOps.scala b/core/src/main/scala/matryoshka/IdOps.scala index 87b3f18d..ed2496a1 100644 --- a/core/src/main/scala/matryoshka/IdOps.scala +++ b/core/src/main/scala/matryoshka/IdOps.scala @@ -19,7 +19,7 @@ package matryoshka import scalaz._ sealed class IdOps[A](self: A) { - def hylo[F[_]: Functor, B](f: F[B] => B, g: A => F[A]): B = + def hylo[F[_]: Functor, B](f: Algebra[F, B], g: Coalgebra[F, A]): B = matryoshka.hylo(self)(f, g) def hyloM[M[_]: Monad, F[_]: Traverse, B](f: F[B] => M[B], g: A => M[F[A]]): M[B] = @@ -47,7 +47,7 @@ sealed class IdOps[A](self: A) { def elgotAna[F[_]: Functor, B](ψ: A => B \/ F[A]): Free[F, B] = matryoshka.elgotAna(self)(ψ) - def elgot[F[_]: Functor, B](φ: F[B] => B, ψ: A => B \/ F[A]): B = + def elgot[F[_]: Functor, B](φ: Algebra[F, B], ψ: ElgotCoalgebra[B \/ ?, F, A]): B = matryoshka.elgot(self)(φ, ψ) def coelgot[F[_]: Functor, B](φ: ((A, F[B])) => B, ψ: A => F[A]): B = diff --git a/core/src/main/scala/matryoshka/algebra.scala b/core/src/main/scala/matryoshka/algebra.scala new file mode 100644 index 00000000..c3f28f2a --- /dev/null +++ b/core/src/main/scala/matryoshka/algebra.scala @@ -0,0 +1,115 @@ +/* + * Copyright 2014 - 2015 SlamData Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package matryoshka + +import scala.Function1 + +import scalaz._, Scalaz._ + +/** The most general algebra, using both generalized and Elgot comonads as + * well as a monad. + */ +final class GElgotAlgebraM[E[_], G[_], M[_], F[_], A](f: E[F[G[A]]] => M[A]) + extends Function1[E[F[G[A]]], M[A]] { + def apply(v1: E[F[G[A]]]) = f(v1) +} +object GElgotAlgebraM { + implicit def zip[E[_]: Functor, G[_]: Functor, M[_]: Applicative, F[_]: Functor]: + Zip[GElgotAlgebraM[E, G, M, F, ?]] = + new Zip[GElgotAlgebraM[E, G, M, F, ?]] { + def zip[A, B]( + a: ⇒ GElgotAlgebraM[E, G, M, F, A], + b: ⇒ GElgotAlgebraM[E, G, M, F, B]) = + node => (a(node ∘ (_ ∘ (_ ∘ (_._1)))) ⊛ b(node ∘ (_ ∘ (_ ∘ (_._2)))))((_, _)) + } +} + +sealed class GElgotCoalgebraM[E[_], G[_], M[_], F[_], A](f: A => M[E[F[G[A]]]]) + extends Function1[A, M[E[F[G[A]]]]] { + def apply(v1: A) = f(v1) +} + +// NB: This class is needed to avoid the `Id[Id[_]]` “cyclic alias” issue. +final class GCoalgebra[G[_], F[_], A](f: A => F[G[A]]) + extends GElgotCoalgebraM[Id, G, Id, F, A](f) + +sealed trait ZeroIdInstances { + implicit def toGElgotAlgebraM[E[_], G[_], M[_], F[_], A](f: E[F[G[A]]] => M[A]): GElgotAlgebraM[E, G, M, F, A] = + new GElgotAlgebraM[E, G, M, F, A](f) + + implicit def toGElgotAlgebraMOps[E[_], G[_], M[_], F[_], A]( + a: GElgotAlgebraM[E, G, M, F, A]): + GElgotAlgebraMOps[E, G, M, F, A] = + new GElgotAlgebraMOps[E, G, M, F, A](a) + + implicit def toGElgotCoalgebraM[E[_], G[_], M[_], F[_], A](f: A => M[E[F[G[A]]]]): GElgotCoalgebraM[E, G, M, F, A] = + new GElgotCoalgebraM[E, G, M, F, A](f) + +} + +sealed trait OneIdInstances extends ZeroIdInstances { + implicit def toGElgotAlgebraOps[E[_], G[_], F[_], A]( + a: GElgotAlgebra[E, G, F, A]): + GElgotAlgebraOps[E, G, F, A] = + new GElgotAlgebraOps[E, G, F, A](a) + + implicit def toElgotAlgebraMOps[E[_], M[_], F[_], A]( + a: ElgotAlgebraM[E, M, F, A]): + ElgotAlgebraMOps[E, M, F, A] = + new ElgotAlgebraMOps[E, M, F, A](a) + + implicit def toGAlgebraMOps[G[_], M[_], F[_], A](a: GAlgebraM[G, M, F, A]): + GAlgebraMOps[G, M, F, A] = + new GAlgebraMOps[G, M, F, A](a) + + + implicit def toGElgotCoalgebraOps[E[_], G[_], F[_], A]( + a: GElgotCoalgebra[E, G, F, A]): + GElgotCoalgebraOps[E, G, F, A] = + new GElgotCoalgebraOps[E, G, F, A](a) + + implicit def toElgotCoalgebraMOps[E[_], M[_], F[_], A]( + a: ElgotCoalgebraM[E, M, F, A]): + ElgotCoalgebraMOps[E, M, F, A] = + new ElgotCoalgebraMOps[E, M, F, A](a) + + implicit def toGCoalgebraMOps[G[_], M[_], F[_], A](a: GCoalgebraM[G, M, F, A]): + GCoalgebraMOps[G, M, F, A] = + new GCoalgebraMOps[G, M, F, A](a) +} + +sealed trait TwoIdInstances extends OneIdInstances { + // implicit def toElgotAlgebraOps[W[_], F[_], A](a: ElgotAlgebra[W, F, A]): + // ElgotAlgebraOps[W, F, A] = + // new ElgotAlgebraOps[W, F, A](a) + + // // implicit def toGAlgebraOps[W[_], F[_], A](a: GAlgebra[W, F, A]): + // // GAlgebraOps[W, F, A] = + // // new GAlgebraOps[W, F, A](a) + + // implicit def toAlgebraMOps[M[_], F[_], A](a: AlgebraM[M, F, A]): + // AlgebraMOps[M, F, A] = + // new AlgebraMOps[M, F, A](a) +} + +trait ThreeIdInstances extends TwoIdInstances { + implicit def toAlgebra[F[_], A](f: F[A] => A): Algebra[F, A] = + new GElgotAlgebraM[Id, Id, Id, F, A](f) + + implicit def toCoalgebra[F[_], A](f: A => F[A]): Coalgebra[F, A] = + new GCoalgebra[Id, F, A](f) +} diff --git a/core/src/main/scala/matryoshka/package.scala b/core/src/main/scala/matryoshka/package.scala index 3979ebee..0d43e84f 100644 --- a/core/src/main/scala/matryoshka/package.scala +++ b/core/src/main/scala/matryoshka/package.scala @@ -40,7 +40,8 @@ import simulacrum.typeclass * constructing an intermediate fixed point structure. * @groupname Dist DistributiveLaws */ -package object matryoshka extends CofreeInstances with FreeInstances { +package object matryoshka + extends ThreeIdInstances with CofreeInstances with FreeInstances { def lambek[T[_[_]]: Corecursive: Recursive, F[_]: Functor](tf: T[F]): F[T[F]] = @@ -50,100 +51,27 @@ package object matryoshka extends CofreeInstances with FreeInstances { T[F] = ft.ana(_ ∘ (_.project)) - type GAlgebraM[W[_], M[_], F[_], A] = F[W[A]] => M[A] - object GAlgebraM { - implicit def toOps[W[_], M[_], F[_], A](a: GAlgebraM[W, M, F, A]): - GAlgebraMOps[W, M, F, A] = - new GAlgebraMOps[W, M, F, A](a) - } - + type GElgotAlgebra[E[_], G[_], F[_], A] = GElgotAlgebraM[E, G, Id, F, A] // E[F[G[A]]] => A + type GAlgebraM[G[_], M[_], F[_], A] = GElgotAlgebraM[Id, G, M, F, A] // F[W[A]] => M[A] type GAlgebra[W[_], F[_], A] = GAlgebraM[W, Id, F, A] // F[W[A]] => A - object GAlgebra { - implicit def zip[W[_]: Functor, F[_]: Functor]: Zip[GAlgebra[W, F, ?]] = - new Zip[GAlgebra[W, F, ?]] { - def zip[A, B](a: ⇒ GAlgebra[W, F, A], b: ⇒ GAlgebra[W, F, B]) = - node => (a(node ∘ (_ ∘ (_._1))), b(node ∘ (_ ∘ (_._2)))) - } - - implicit def toOps[W[_], F[_], A](a: GAlgebra[W, F, A]): GAlgebraOps[W, F, A] = - new GAlgebraOps[W, F, A](a) - } - - type AlgebraM[M[_], F[_], A] = GAlgebraM[Id, M, F, A] // F[A] => M[A] - object AlgebraM { - implicit def toOps[M[_], F[_], A](a: AlgebraM[M, F, A]): - AlgebraMOps[M, F, A] = - new AlgebraMOps[M, F, A](a) - } - + type AlgebraM[M[_], F[_], A] = GElgotAlgebraM[Id, Id, M, F, A] // F[A] => M[A] type Algebra[F[_], A] = GAlgebra[Id, F, A] // F[A] => A - object Algebra { - implicit def zip[F[_]: Functor] = GAlgebra.zip[Id, F] - - implicit def toOps[F[_], A](a: Algebra[F, A]): AlgebraOps[F, A] = - new AlgebraOps[F, A](a) - } - - type ElgotAlgebraM[W[_], M[_], F[_], A] = W[F[A]] => M[A] - object ElgotAlgebraM { - implicit def zip[W[_]: Functor, M[_]: Applicative, F[_]: Functor]: - Zip[ElgotAlgebraM[W, M, F, ?]] = - new Zip[ElgotAlgebraM[W, M, F, ?]] { - def zip[A, B](a: ⇒ ElgotAlgebraM[W, M, F, A], b: ⇒ ElgotAlgebraM[W, M, F, B]) = - w => Bitraverse[(?, ?)].bisequence((a(w ∘ (_ ∘ (_._1))), b(w ∘ (_ ∘ (_._2))))) - } - - implicit def toOps[W[_], M[_], F[_], A](a: ElgotAlgebraM[W, M, F, A]): - ElgotAlgebraMOps[W, M, F, A] = - new ElgotAlgebraMOps[W, M, F, A](a) - } - + type ElgotAlgebraM[E[_], M[_], F[_], A] = GElgotAlgebraM[E, Id, M, F, A] // W[F[A]] => M[A] type ElgotAlgebra[W[_], F[_], A] = ElgotAlgebraM[W, Id, F, A] // W[F[A]] => A - object ElgotAlgebra { - implicit def zip[W[_]: Functor, F[_]: Functor] = ElgotAlgebraM.zip[W, Id, F] - // (ann, node) => node.unfzip.bimap(f(ann, _), g(ann, _)) - - implicit def toOps[W[_], F[_], A](a: ElgotAlgebra[W, F, A]): - ElgotAlgebraOps[W, F, A] = - new ElgotAlgebraOps[W, F, A](a) - } + type GElgotCoalgebra[E[_], G[_], F[_], A] = GElgotCoalgebraM[E, G, Id, F, A] /** A coalgebra with two monads, one handled by the fold and the other in the * result. `N` is the fold’s monad, whereas `M` is the Kleisli. */ - type GCoalgebraM[N[_], M[_], F[_], A] = A => M[F[N[A]]] - - type GCoalgebra[N[_], F[_], A] = GCoalgebraM[N, Id, F, A] // A => F[N[A]] - object GCoalgebra { - implicit def toOps[M[_], F[_], A](a: GCoalgebra[M, F, A]): - GCoalgebraOps[M, F, A] = - new GCoalgebraOps[M, F, A](a) - } - + type GCoalgebraM[G[_], M[_], F[_], A] = GElgotCoalgebraM[Id, G, M, F, A] // A => M[F[G[A]]] + // type GCoalgebra[N[_], F[_], A] = GCoalgebraM[N, Id, F, A] // A => F[N[A]] type CoalgebraM[M[_], F[_], A] = GCoalgebraM[Id, M, F, A] // A => M[F[A]] - object CoalgebraM { - implicit def toOps[M[_], F[_], A](a: CoalgebraM[M, F, A]): - CoalgebraMOps[M, F, A] = - new CoalgebraMOps[M, F, A](a) - } - - type Coalgebra[F[_], A] = GCoalgebra[Id, F, A] // A => F[A] - object Coalgebra { - implicit def toOps[F[_], A](a: Coalgebra[F, A]): CoalgebraOps[F, A] = - new CoalgebraOps[F, A](a) - } - - type ElgotCoalgebraM[N[_], M[_], F[_], A] = A => M[N[F[A]]] - + type Coalgebra[F[_], A] = GCoalgebra[Id, F, A] // A => F[A] + type ElgotCoalgebraM[E[_], M[_], F[_], A] = GElgotCoalgebraM[E, Id, M, F, A] // A => M[E[F[A]]] /** Identical to [[matryoshka.CoalgebraM]] in structure, this signifies a * different intent – the dual of [[matryoshka.ElgotAlgebra]]. */ type ElgotCoalgebra[N[_], F[_], A] = ElgotCoalgebraM[N, Id, F, A] // A => N[F[A]] - object ElgotCoalgebra { - implicit def toOps[M[_], F[_], A](a: ElgotCoalgebra[M, F, A]): - ElgotCoalgebraOps[M, F, A] = - new ElgotCoalgebraOps[M, F, A](a) - } /** A `NaturalTransformation` that sequences two types. * @@ -229,8 +157,8 @@ package object matryoshka extends CofreeInstances with FreeInstances { * * @group Refold */ - def hylo[F[_]: Functor, A, B](a: A)(f: F[B] => B, g: A => F[A]): B = - f(g(a) ∘ (hylo(_)(f, g))) + def hylo[F[_]: Functor, A, B](a: A)(f: Algebra[F, B], g: Coalgebra[F, A]): B = + f(g(a).join.copoint ∘ (hylo[F, A, B](_)(f, g))) /** A Kleisli [[matryoshka.hylo]]. * @@ -273,7 +201,7 @@ package object matryoshka extends CofreeInstances with FreeInstances { * * @group Refold */ - def elgot[F[_]: Functor, A, B](a: A)(φ: F[B] => B, ψ: A => B \/ F[A]): B = { + def elgot[F[_]: Functor, A, B](a: A)(φ: Algebra[F, B], ψ: ElgotCoalgebra[B \/ ?, F, A]): B = { def h: A => B = (((x: B) => x) ||| ((x: F[A]) => φ(x ∘ h))) ⋘ ψ h(a) @@ -404,19 +332,11 @@ package object matryoshka extends CofreeInstances with FreeInstances { if (index < 0) None else fa.toList.drop(index).headOption - /** Turns any F-algebra, into an identical one that attributes the tree with - * the results for each node. */ - def attributeAlgebraM[M[_]: Functor, F[_]: Functor, A](f: AlgebraM[M, F, A]): - AlgebraM[M, F, Cofree[F, A]] = - fa => f(fa ∘ (_.head)) ∘ (Cofree(_, fa)) - - def attributeAlgebra[F[_]: Functor, A](f: Algebra[F, A]) = - attributeAlgebraM[Id, F, A](f) - - def attrK[F[_]: Functor, A](k: A) = attributeAlgebra[F, A](Function.const(k)) + def attrK[F[_]: Functor, A](k: A) = + attribute(new GElgotAlgebraM[Id, Id, Id, F, A](Function.const(k))) def attrSelf[T[_[_]]: Corecursive, F[_]: Functor] = - attributeAlgebra[F, T[F]](_.embed) + attribute(new GElgotAlgebraM[Id, Id, Id, F, T[F]](_.embed)) /** NB: Since Cofree carries the functor, the resulting algebra is a cata, not * a para. */ @@ -424,28 +344,21 @@ package object matryoshka extends CofreeInstances with FreeInstances { F[Cofree[F, A]] => Cofree[F, A] = fa => Cofree(f(fa ∘ (x => (Recursive[Cofree[?[_], A]].convertTo[F, T](x), x.head))), fa) - def attributeGAlgebraM[W[_]: Comonad, M[_]: Functor, F[_]: Functor, A]( - f: GAlgebraM[W, M, F, A]): - GAlgebraM[W, M, F, Cofree[F, A]] = - node => f(node ∘ (_ ∘ (_.head))) ∘ (Cofree(_, node ∘ (_.copoint))) - - def attributeElgotAlgebraM[W[_]: Comonad, M[_]: Functor, F[_]: Functor, A]( - f: ElgotAlgebraM[W, M, F, A]): - ElgotAlgebraM[W, M, F, Cofree[F, A]] = - node => f(node ∘ (_ ∘ (_.head))) ∘ (Cofree(_, node.copoint)) + /** Turns any F-algebra, into an identical one that attributes the tree with + * the results for each node. */ + def attribute[E[_]: Comonad, G[_]: Comonad, M[_]: Functor, F[_]: Functor, A]( + f: GElgotAlgebraM[E, G, M, F, A]): + GElgotAlgebraM[E, G, M, F, Cofree[F, A]] = + node => f(node ∘ (_ ∘ (_ ∘ (_.head)))) ∘ (Cofree(_, node.copoint ∘ (_.copoint))) def generalizeM[M[_]: Applicative, A, B](f: A => B): A => M[B] = f(_).point[M] def generalizeW[W[_]: Comonad, A, B](f: A => B): W[A] => B = w => f(w.copoint) - def generalizeAlgebra[W[_]: Comonad, F[_]: Functor, A, B](f: F[A] => B): - F[W[A]] => B = + def generalizeAlgebra[O[_]: Functor, X[_]: Comonad, I[_], M[_], F[_], A](f: O[I[A]] => M[A]): + O[X[I[A]]] => M[A] = node => f(node ∘ (_.copoint)) - def generalizeCoalgebra[M[_]: Applicative, F[_]: Functor, A](f: A => F[A]): - A => F[M[A]] = - f(_).map(_.point[M]) - def generalizeCoalgebraM[M[_]: Functor, N[_]: Applicative, F[_]: Functor, A]( - f: A => M[F[A]]): - A => M[F[N[A]]] = - f(_).map(_.map(_.point[N])) + def generalizeCoalgebra[O[_]: Functor, X[_]: Applicative, I[_], A](f: A => O[I[A]]): + A => O[X[I[A]]] = + f(_).map(_.point[X]) /** Repeatedly applies the function to the result as long as it returns Some. * Finally returns the last non-None value (which may be the initial input). diff --git a/core/src/test/scala/matryoshka/spec.scala b/core/src/test/scala/matryoshka/spec.scala index f67cde87..dace16d5 100644 --- a/core/src/test/scala/matryoshka/spec.scala +++ b/core/src/test/scala/matryoshka/spec.scala @@ -97,11 +97,6 @@ object Exp { } implicit def ExpEqual2[A](implicit A: Equal[A]): Equal[Exp[A]] = ExpEqual(A) - // NB: Something like this currently needs to be defined for any Functor in - // order to get the generalize operations for the algebra. - implicit def toOps[A](a: Algebra[Exp, A]): AlgebraOps[Exp, A] = - Algebra.toOps[Exp, A](a) - implicit val ExpShow: Show ~> λ[α => Show[Exp[α]]] = new (Show ~> λ[α => Show[Exp[α]]]) { def apply[α](show: Show[α]) = @@ -438,9 +433,9 @@ class FixplateSpecs extends Specification with ScalaCheck with ScalazMatchers { "zipAlgebras" should { "both eval and find all constants" in { - mul(num(5), num(2)).cata(Algebra.zip[Exp].zip(eval, findConstants)) must + mul(num(5), num(2)).cata(GElgotAlgebraM.zip[Id, Id, Id, Exp].zip(eval, findConstants)) must equal((10, List(5, 2))) - mul(num(5), num(2)).convertTo[Mu].cata(Algebra.zip[Exp].zip(eval, findConstants)) must + mul(num(5), num(2)).convertTo[Mu].cata(GElgotAlgebraM.zip[Id, Id, Id, Exp].zip(eval, findConstants)) must equal((10, List(5, 2))) } } @@ -479,9 +474,11 @@ class FixplateSpecs extends Specification with ScalaCheck with ScalazMatchers { } } - def extractFactors: Coalgebra[Exp, Int] = x => + // TODO: Why do we need to explicitly call the implicit here? Is it because + // `F` is in covariant position? + def extractFactors: Coalgebra[Exp, Int] = toCoalgebra(x => if (x > 2 && x % 2 == 0) Mul(2, x/2) - else Num(x) + else Num(x)) "generalizeCoalgebra" should { "behave like ana" ! prop { (i: Int) => From 64244bbd818f8507b5d6411fcc199ff997030f24 Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Fri, 11 Mar 2016 23:16:10 -0700 Subject: [PATCH 4/8] Delete some dead code. --- core/src/main/scala/matryoshka/algebra.scala | 11 ----------- core/src/main/scala/matryoshka/package.scala | 1 - 2 files changed, 12 deletions(-) diff --git a/core/src/main/scala/matryoshka/algebra.scala b/core/src/main/scala/matryoshka/algebra.scala index c3f28f2a..2ca79e35 100644 --- a/core/src/main/scala/matryoshka/algebra.scala +++ b/core/src/main/scala/matryoshka/algebra.scala @@ -93,17 +93,6 @@ sealed trait OneIdInstances extends ZeroIdInstances { } sealed trait TwoIdInstances extends OneIdInstances { - // implicit def toElgotAlgebraOps[W[_], F[_], A](a: ElgotAlgebra[W, F, A]): - // ElgotAlgebraOps[W, F, A] = - // new ElgotAlgebraOps[W, F, A](a) - - // // implicit def toGAlgebraOps[W[_], F[_], A](a: GAlgebra[W, F, A]): - // // GAlgebraOps[W, F, A] = - // // new GAlgebraOps[W, F, A](a) - - // implicit def toAlgebraMOps[M[_], F[_], A](a: AlgebraM[M, F, A]): - // AlgebraMOps[M, F, A] = - // new AlgebraMOps[M, F, A](a) } trait ThreeIdInstances extends TwoIdInstances { diff --git a/core/src/main/scala/matryoshka/package.scala b/core/src/main/scala/matryoshka/package.scala index 0d43e84f..10240f5b 100644 --- a/core/src/main/scala/matryoshka/package.scala +++ b/core/src/main/scala/matryoshka/package.scala @@ -64,7 +64,6 @@ package object matryoshka * result. `N` is the fold’s monad, whereas `M` is the Kleisli. */ type GCoalgebraM[G[_], M[_], F[_], A] = GElgotCoalgebraM[Id, G, M, F, A] // A => M[F[G[A]]] - // type GCoalgebra[N[_], F[_], A] = GCoalgebraM[N, Id, F, A] // A => F[N[A]] type CoalgebraM[M[_], F[_], A] = GCoalgebraM[Id, M, F, A] // A => M[F[A]] type Coalgebra[F[_], A] = GCoalgebra[Id, F, A] // A => F[A] type ElgotCoalgebraM[E[_], M[_], F[_], A] = GElgotCoalgebraM[E, Id, M, F, A] // A => M[E[F[A]]] From 2d10b6a1f5fbb81cd5c7556efe8fc867b835e289 Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Sun, 13 Mar 2016 09:43:29 -0600 Subject: [PATCH 5/8] Convert a few more things to {co}algebra types. --- core/src/main/scala/matryoshka/IdOps.scala | 8 ++-- core/src/main/scala/matryoshka/algebra.scala | 47 ++++++++++++++++++++ core/src/main/scala/matryoshka/package.scala | 16 +++---- core/src/test/scala/matryoshka/spec.scala | 7 +-- 4 files changed, 63 insertions(+), 15 deletions(-) diff --git a/core/src/main/scala/matryoshka/IdOps.scala b/core/src/main/scala/matryoshka/IdOps.scala index ed2496a1..de44ad8b 100644 --- a/core/src/main/scala/matryoshka/IdOps.scala +++ b/core/src/main/scala/matryoshka/IdOps.scala @@ -21,19 +21,19 @@ import scalaz._ sealed class IdOps[A](self: A) { def hylo[F[_]: Functor, B](f: Algebra[F, B], g: Coalgebra[F, A]): B = matryoshka.hylo(self)(f, g) - def hyloM[M[_]: Monad, F[_]: Traverse, B](f: F[B] => M[B], g: A => M[F[A]]): + def hyloM[M[_]: Monad, F[_]: Traverse, B](f: AlgebraM[M, F, B], g: CoalgebraM[M, F, A]): M[B] = matryoshka.hyloM(self)(f, g) def ghylo[F[_]: Functor, W[_]: Comonad, M[_]: Monad, B]( w: DistributiveLaw[F, W], m: DistributiveLaw[M, F], - f: F[W[B]] => B, - g: A => F[M[A]]): + f: GAlgebra[W, F, B], + g: GCoalgebra[M, F, A]): B = matryoshka.ghylo(self)(w, m, f, g) def chrono[F[_]: Functor, B]( - g: F[Cofree[F, B]] => B, f: A => F[Free[F, A]]): + g: GAlgebra[Cofree[F, ?], F, B], f: GCoalgebra[Free[F, ?], F, A]): B = matryoshka.chrono(self)(g, f) diff --git a/core/src/main/scala/matryoshka/algebra.scala b/core/src/main/scala/matryoshka/algebra.scala index 2ca79e35..f87560eb 100644 --- a/core/src/main/scala/matryoshka/algebra.scala +++ b/core/src/main/scala/matryoshka/algebra.scala @@ -93,6 +93,53 @@ sealed trait OneIdInstances extends ZeroIdInstances { } sealed trait TwoIdInstances extends OneIdInstances { + // FIXME: somehow this causes an ambiguous implicit with a lower priority + // implicit. + // implicit def toElgotAlgebra[E[_], F[_], A](f: E[F[A]] => A): + // ElgotAlgebra[E, F, A] = + // new GElgotAlgebraM[E, Id, Id, F, A](f) + // Poor man’s unapply trick + implicit def toElgotAlgebraU[E[_[_], _], F[_], A, X[_]](f: E[X, F[A]] => A): + ElgotAlgebra[E[X, ?], F, A] = + new GElgotAlgebraM[E[X, ?], Id, Id, F, A](f) + + implicit def toElgotCoalgebra[E[_], F[_], A](f: A => E[F[A]]): + ElgotCoalgebra[E, F, A] = + new GElgotCoalgebraM[E, Id, Id, F, A](f) + // Poor man’s unapply trick + implicit def toElgotCoalgebraU[E[_[_], _], F[_], A, X[_]](f: A => E[X, F[A]]): + ElgotCoalgebra[E[X, ?], F, A] = + new GElgotCoalgebraM[E[X, ?], Id, Id, F, A](f) + + implicit def toGAlgebra[G[_], F[_], A](f: F[G[A]] => A): GAlgebra[G, F, A] = + new GElgotAlgebraM[Id, G, Id, F, A](f) + // Poor man’s unapply trick + implicit def toGAlgebraU[G[_[_], _], F[_], A, X[_]](f: F[G[X, A]] => A): + GAlgebra[G[X, ?], F, A] = + new GElgotAlgebraM[Id, G[X, ?], Id, F, A](f) + + implicit def toGCoalgebra[G[_], F[_], A](f: A => F[G[A]]): + GCoalgebra[G, F, A] = + new GCoalgebra[G, F, A](f) + // Poor man’s unapply trick + implicit def toGCoalgebraU[G[_[_], _], F[_], A, X[_]](f: A => F[G[X, A]]): + GCoalgebra[G[X, ?], F, A] = + new GCoalgebra[G[X, ?], F, A](f) + + implicit def toAlgebraM[M[_], F[_], A](f: F[A] => M[A]): AlgebraM[M, F, A] = + new GElgotAlgebraM[Id, Id, M, F, A](f) + // Poor man’s unapply trick + implicit def toAlgebraMU[M[_[_], _], F[_], A, X[_]](f: F[A] => M[X, A]): + AlgebraM[M[X, ?], F, A] = + new GElgotAlgebraM[Id, Id, M[X, ?], F, A](f) + + implicit def toCoalgebraM[M[_], F[_], A](f: A => M[F[A]]): + CoalgebraM[M, F, A] = + new GElgotCoalgebraM[Id, Id, M, F, A](f) + // Poor man’s unapply trick + implicit def toCoalgebraMU[M[_[_], _], F[_], A, X[_]](f: A => M[X, F[A]]): + CoalgebraM[M[X, ?], F, A] = + new GElgotCoalgebraM[Id, Id, M[X, ?], F, A](f) } trait ThreeIdInstances extends TwoIdInstances { diff --git a/core/src/main/scala/matryoshka/package.scala b/core/src/main/scala/matryoshka/package.scala index 10240f5b..7514472f 100644 --- a/core/src/main/scala/matryoshka/package.scala +++ b/core/src/main/scala/matryoshka/package.scala @@ -139,15 +139,15 @@ package object matryoshka M[B] = t.tail.traverse(cofCataM(_)(f)) >>= (f(t.head, _)) - /** This is the “second” fold from `paraElgotZygo`, with the comonad + /** This is the “second” fold from `paraElgotZygoM`, with the comonad * structures coming from `Cofree` rather than from another algebra. * * @group Fold */ def cofParaM[M[_]] = new CofParaMPartiallyApplied[M] 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.traverseU(cs => self(cs)(f) ∘ ((Recursive[Cofree[?[_], A]].convertTo[S, T](cs), _))) >>= (f(t.head, _)) + def apply[T[_[_]]: Corecursive, S[_]: Traverse, A, B](t: Cofree[S, A])(f: GElgotAlgebraM[(A, ?), (T[S], ?), M, S, B])(implicit M: Monad[M]): M[B] = + t.tail.traverseU(cs => self(cs)(f) ∘ ((Recursive[Cofree[?[_], A]].convertTo[S, T](cs), _))) >>= (x => f((t.head, x))) } /** A hylomorphism is a composition of [[matryoshka.Corecursive.ana]] and @@ -163,9 +163,9 @@ package object matryoshka * * @group Refold */ - def hyloM[M[_]: Monad, F[_]: Traverse, A, B](a: A)(f: F[B] => M[B], g: A => M[F[A]]): + def hyloM[M[_]: Monad, F[_]: Traverse, A, B](a: A)(f: AlgebraM[M, F, B], g: CoalgebraM[M, F, A]): M[B] = - g(a) >>= (_.traverse(hyloM(_)(f, g)) >>= (f)) + g(a) >>= (_.join.copoint.traverse(hyloM[M, F, A, B](_)(f, g)) >>= f) /** A generalized version of [[matryoshka.hylo]] that composes any coalgebra * and algebra. @@ -176,8 +176,8 @@ package object matryoshka a: A)( w: DistributiveLaw[F, W], m: DistributiveLaw[M, F], - f: F[W[B]] => B, - g: A => F[M[A]])( + f: GAlgebra[W, F, B], + g: GCoalgebra[M, F, A])( implicit M: Monad[M]): B = { def h(x: M[A]): W[B] = w(m(M.lift(g)(x)) ∘ (y => h(y.join).cojoin)) ∘ f @@ -191,7 +191,7 @@ package object matryoshka */ def chrono[F[_]: Functor, A, B]( a: A)( - g: F[Cofree[F, B]] => B, f: A => F[Free[F, A]]): + g: GAlgebra[Cofree[F, ?], F, B], f: GCoalgebra[Free[F, ?], F, A]): B = ghylo[F, Cofree[F, ?], Free[F, ?], A, B](a)(distHisto, distFutu, g, f) diff --git a/core/src/test/scala/matryoshka/spec.scala b/core/src/test/scala/matryoshka/spec.scala index dace16d5..352bd384 100644 --- a/core/src/test/scala/matryoshka/spec.scala +++ b/core/src/test/scala/matryoshka/spec.scala @@ -744,8 +744,8 @@ class FixplateSpecs extends Specification with ScalaCheck with ScalazMatchers { } // NB: This is better done with cata, but we fake it here - def partialEval[T[_[_]]: Corecursive: Recursive](t: Exp[Cofree[Exp, T[Exp]]]): - T[Exp] = + def partialEval[T[_[_]]: Corecursive: Recursive]: + Exp[Cofree[Exp, T[Exp]]] => T[Exp] = t => t match { case Mul(x, y) => (x.head.project, y.head.project) match { case (Num(a), Num(b)) => Num[T[Exp]](a * b).embed @@ -768,7 +768,8 @@ class FixplateSpecs extends Specification with ScalaCheck with ScalazMatchers { } } - def extract2and3(x: Int): Exp[Free[Exp, Int]] = + // FIXME: defining this as an algebra brings up the “cyclic aliasisng” issue + def extract2and3: Int => Exp[Free[Exp, Int]] = x => // factors all the way down if (x > 2 && x % 2 == 0) Mul(Free.point(2), Free.point(x/2)) // factors once and then stops From dca70ca6dd270b541228cc059b7e0521ea8f9a2b Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Mon, 14 Mar 2016 11:58:55 -0600 Subject: [PATCH 6/8] =?UTF-8?q?Apply=20most=20of=20@wemrysi=E2=80=99s=20re?= =?UTF-8?q?commendations.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/scala/matryoshka/AlgebraOps.scala | 7 ---- .../src/main/scala/matryoshka/CofreeOps.scala | 34 ------------------- core/src/main/scala/matryoshka/FreeOps.scala | 24 ------------- core/src/main/scala/matryoshka/algebra.scala | 19 +++++++---- core/src/main/scala/matryoshka/cofree.scala | 3 -- core/src/main/scala/matryoshka/free.scala | 3 -- core/src/main/scala/matryoshka/package.scala | 20 +++++++++++ core/src/test/scala/matryoshka/spec.scala | 4 +-- 8 files changed, 34 insertions(+), 80 deletions(-) delete mode 100644 core/src/main/scala/matryoshka/CofreeOps.scala delete mode 100644 core/src/main/scala/matryoshka/FreeOps.scala diff --git a/core/src/main/scala/matryoshka/AlgebraOps.scala b/core/src/main/scala/matryoshka/AlgebraOps.scala index bf06ab6f..4a30acf9 100644 --- a/core/src/main/scala/matryoshka/AlgebraOps.scala +++ b/core/src/main/scala/matryoshka/AlgebraOps.scala @@ -18,13 +18,6 @@ package matryoshka import scalaz._, Scalaz._ -final class GElgotAlgebraMOps[E[_], G[_], M[_], F[_], A]( - self: GElgotAlgebraM[E, G, M, F, A]) { - - def attribute(implicit E: Comonad[E], G: Comonad[G], M: Functor[M], F: Functor[F]) = - matryoshka.attribute[E, G, M, F, A](self) -} - final class GAlgebraMOps[G[_], M[_], F[_], A](self: GAlgebraM[G, M, F, A]) { def generalizeElgot[E[_]: Comonad]: GElgotAlgebraM[E, G, M, F, A] = matryoshka.generalizeW[E, F[G[A]], M[A]](self) diff --git a/core/src/main/scala/matryoshka/CofreeOps.scala b/core/src/main/scala/matryoshka/CofreeOps.scala deleted file mode 100644 index e56db101..00000000 --- a/core/src/main/scala/matryoshka/CofreeOps.scala +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2014 - 2015 SlamData Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package matryoshka - -import scalaz._ - -sealed class CofreeOps[F[_], A](self: Cofree[F, A]) { - def elgotCata[B](φ: ((A, F[B])) => B)(implicit F: Functor[F]): B = - matryoshka.elgotCata(self)(φ) - - def elgotCataM[M[_]: Monad, B]( - φ: ((A, F[B])) => M[B])( - implicit F: Traverse[F]): - M[B] = - matryoshka.elgotCataM(self)(φ) - - def cofCataM[M[_]: Monad, B](f: (A, F[B]) => M[B])(implicit F: Traverse[F]): - M[B] = - matryoshka.cofCataM(self)(f) -} diff --git a/core/src/main/scala/matryoshka/FreeOps.scala b/core/src/main/scala/matryoshka/FreeOps.scala deleted file mode 100644 index ed27319a..00000000 --- a/core/src/main/scala/matryoshka/FreeOps.scala +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright 2014 - 2015 SlamData Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package matryoshka - -import scalaz._ - -sealed class FreeOps[F[_], A](self: Free[F, A]) { - def interpretCata(φ: F[A] => A)(implicit F: Functor[F]): A = - matryoshka.interpretCata(self)(φ) -} diff --git a/core/src/main/scala/matryoshka/algebra.scala b/core/src/main/scala/matryoshka/algebra.scala index f87560eb..4d4c4cab 100644 --- a/core/src/main/scala/matryoshka/algebra.scala +++ b/core/src/main/scala/matryoshka/algebra.scala @@ -26,15 +26,25 @@ import scalaz._, Scalaz._ final class GElgotAlgebraM[E[_], G[_], M[_], F[_], A](f: E[F[G[A]]] => M[A]) extends Function1[E[F[G[A]]], M[A]] { def apply(v1: E[F[G[A]]]) = f(v1) + + def attribute(implicit E: Comonad[E], G: Comonad[G], M: Functor[M], F: Functor[F]) = + matryoshka.attribute[E, G, M, F, A](this) + + def zip[B]( + b: ⇒ GElgotAlgebraM[E, G, M, F, B])( + implicit E: Functor[E], G: Functor[G], M: Applicative[M], F: Functor[F]): + GElgotAlgebraM[E, G, M, F, (A, B)]= + node => (this.f(node ∘ (_ ∘ (_ ∘ (_._1)))) ⊛ b(node ∘ (_ ∘ (_ ∘ (_._2)))))((_, _)) + } object GElgotAlgebraM { - implicit def zip[E[_]: Functor, G[_]: Functor, M[_]: Applicative, F[_]: Functor]: + implicit def gElgotAlgebraMZip[E[_]: Functor, G[_]: Functor, M[_]: Applicative, F[_]: Functor]: Zip[GElgotAlgebraM[E, G, M, F, ?]] = new Zip[GElgotAlgebraM[E, G, M, F, ?]] { def zip[A, B]( a: ⇒ GElgotAlgebraM[E, G, M, F, A], b: ⇒ GElgotAlgebraM[E, G, M, F, B]) = - node => (a(node ∘ (_ ∘ (_ ∘ (_._1)))) ⊛ b(node ∘ (_ ∘ (_ ∘ (_._2)))))((_, _)) + a.zip(b) } } @@ -51,11 +61,6 @@ sealed trait ZeroIdInstances { implicit def toGElgotAlgebraM[E[_], G[_], M[_], F[_], A](f: E[F[G[A]]] => M[A]): GElgotAlgebraM[E, G, M, F, A] = new GElgotAlgebraM[E, G, M, F, A](f) - implicit def toGElgotAlgebraMOps[E[_], G[_], M[_], F[_], A]( - a: GElgotAlgebraM[E, G, M, F, A]): - GElgotAlgebraMOps[E, G, M, F, A] = - new GElgotAlgebraMOps[E, G, M, F, A](a) - implicit def toGElgotCoalgebraM[E[_], G[_], M[_], F[_], A](f: A => M[E[F[G[A]]]]): GElgotCoalgebraM[E, G, M, F, A] = new GElgotCoalgebraM[E, G, M, F, A](f) diff --git a/core/src/main/scala/matryoshka/cofree.scala b/core/src/main/scala/matryoshka/cofree.scala index 3a37f21f..5d9da041 100644 --- a/core/src/main/scala/matryoshka/cofree.scala +++ b/core/src/main/scala/matryoshka/cofree.scala @@ -40,9 +40,6 @@ trait CofreeInstances { implicit def cofreeShow[F[_], A: Show](implicit F: (Show ~> λ[α => Show[F[α]]])): Show[Cofree[F, A]] = Show.shows(cof => "(" + cof.head.show + ", " + F(cofreeShow).shows(cof.tail) + ")") - - implicit def toCofreeOps[F[_], A](a: Cofree[F, A]): CofreeOps[F, A] = - new CofreeOps[F, A](a) } object cofree extends CofreeInstances diff --git a/core/src/main/scala/matryoshka/free.scala b/core/src/main/scala/matryoshka/free.scala index c0cd4f8d..9a2cfe26 100644 --- a/core/src/main/scala/matryoshka/free.scala +++ b/core/src/main/scala/matryoshka/free.scala @@ -31,9 +31,6 @@ trait FreeInstances { _.point[Free[G, ?]].point[M], f(_).map(Free.liftF(_).join)) } - - implicit def toFreeOps[F[_], A](a: Free[F, A]): FreeOps[F, A] = - new FreeOps[F, A](a) } object free extends FreeInstances diff --git a/core/src/main/scala/matryoshka/package.scala b/core/src/main/scala/matryoshka/package.scala index 7514472f..4803b440 100644 --- a/core/src/main/scala/matryoshka/package.scala +++ b/core/src/main/scala/matryoshka/package.scala @@ -390,4 +390,24 @@ package object matryoshka implicit def ToCorecursiveOps[T[_[_]]: Corecursive, F[_]](f: F[T[F]]): CorecursiveOps[T, F] = new CorecursiveOps[T, F](f) + + implicit sealed class CofreeOps[F[_], A](self: Cofree[F, A]) { + def elgotCata[B](φ: ((A, F[B])) => B)(implicit F: Functor[F]): B = + matryoshka.elgotCata(self)(φ) + + def elgotCataM[M[_]: Monad, B]( + φ: ((A, F[B])) => M[B])( + implicit F: Traverse[F]): + M[B] = + matryoshka.elgotCataM(self)(φ) + + def cofCataM[M[_]: Monad, B](f: (A, F[B]) => M[B])(implicit F: Traverse[F]): + M[B] = + matryoshka.cofCataM(self)(f) + } + + implicit sealed class FreeOps[F[_], A](self: Free[F, A]) { + def interpretCata(φ: F[A] => A)(implicit F: Functor[F]): A = + matryoshka.interpretCata(self)(φ) + } } diff --git a/core/src/test/scala/matryoshka/spec.scala b/core/src/test/scala/matryoshka/spec.scala index 352bd384..83bc07da 100644 --- a/core/src/test/scala/matryoshka/spec.scala +++ b/core/src/test/scala/matryoshka/spec.scala @@ -433,9 +433,9 @@ class FixplateSpecs extends Specification with ScalaCheck with ScalazMatchers { "zipAlgebras" should { "both eval and find all constants" in { - mul(num(5), num(2)).cata(GElgotAlgebraM.zip[Id, Id, Id, Exp].zip(eval, findConstants)) must + mul(num(5), num(2)).cata(eval.zip(findConstants)) must equal((10, List(5, 2))) - mul(num(5), num(2)).convertTo[Mu].cata(GElgotAlgebraM.zip[Id, Id, Id, Exp].zip(eval, findConstants)) must + mul(num(5), num(2)).convertTo[Mu].cata(eval.zip(findConstants)) must equal((10, List(5, 2))) } } From 89a2247b9a75fe0c4ad341cf5d025a5dad6b92c7 Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Mon, 14 Mar 2016 12:26:19 -0600 Subject: [PATCH 7/8] Remove some Ops classes in favor of Leibniz constraints. --- .../main/scala/matryoshka/AlgebraOps.scala | 35 ----------- .../main/scala/matryoshka/CoalgebraOps.scala | 35 ----------- core/src/main/scala/matryoshka/algebra.scala | 60 +++++++++---------- 3 files changed, 29 insertions(+), 101 deletions(-) delete mode 100644 core/src/main/scala/matryoshka/AlgebraOps.scala delete mode 100644 core/src/main/scala/matryoshka/CoalgebraOps.scala diff --git a/core/src/main/scala/matryoshka/AlgebraOps.scala b/core/src/main/scala/matryoshka/AlgebraOps.scala deleted file mode 100644 index 4a30acf9..00000000 --- a/core/src/main/scala/matryoshka/AlgebraOps.scala +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2014 - 2015 SlamData Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package matryoshka - -import scalaz._, Scalaz._ - -final class GAlgebraMOps[G[_], M[_], F[_], A](self: GAlgebraM[G, M, F, A]) { - def generalizeElgot[E[_]: Comonad]: GElgotAlgebraM[E, G, M, F, A] = - matryoshka.generalizeW[E, F[G[A]], M[A]](self) -} - -final class ElgotAlgebraMOps[E[_], M[_], F[_], A](self: ElgotAlgebraM[E, M, F, A]) { - def generalize[G[_]: Comonad](implicit EF: Functor[λ[α => E[F[α]]]]): - GElgotAlgebraM[E, G, M, F, A] = - matryoshka.generalizeAlgebra[λ[α => E[F[α]]], G, Id, M, F, A](self) -} - -final class GElgotAlgebraOps[E[_], G[_], F[_], A](self: GElgotAlgebra[E, G, F, A]) { - def generalizeM[M[_]: Applicative]: GElgotAlgebraM[E, G, M, F, A] = - matryoshka.generalizeM[M, E[F[G[A]]], A](self) -} diff --git a/core/src/main/scala/matryoshka/CoalgebraOps.scala b/core/src/main/scala/matryoshka/CoalgebraOps.scala deleted file mode 100644 index f9a59e50..00000000 --- a/core/src/main/scala/matryoshka/CoalgebraOps.scala +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2014 - 2015 SlamData Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package matryoshka - -import scalaz._, Scalaz._ - -final class GElgotCoalgebraOps[E[_], G[_], F[_], A](self: GElgotCoalgebra[E, G, F, A]) { - def generalizeM[M[_]: Applicative]: - GElgotCoalgebraM[E, G, M, F, A] = - matryoshka.generalizeM[M, A, E[F[G[A]]]](self) -} - -final class ElgotCoalgebraMOps[E[_], M[_], F[_], A](self: ElgotCoalgebraM[E, M, F, A]) { - def generalize[G[_]: Applicative](implicit MEF: Functor[λ[α => M[E[F[α]]]]]): GElgotCoalgebraM[E, G, M, F, A] = - matryoshka.generalizeCoalgebra[λ[α => M[E[F[α]]]], G, Id, A](self) -} - -final class GCoalgebraMOps[G[_], M[_], F[_], A](self: GCoalgebraM[G, M, F, A]) { - def generalizeElgot[E[_]: Applicative](implicit M: Functor[M]): GElgotCoalgebraM[E, G, M, F, A] = - matryoshka.generalizeCoalgebra[M, E, λ[α => F[G[α]]], A](self) -} diff --git a/core/src/main/scala/matryoshka/algebra.scala b/core/src/main/scala/matryoshka/algebra.scala index 4d4c4cab..0b086fb6 100644 --- a/core/src/main/scala/matryoshka/algebra.scala +++ b/core/src/main/scala/matryoshka/algebra.scala @@ -18,7 +18,7 @@ package matryoshka import scala.Function1 -import scalaz._, Scalaz._ +import scalaz._, Leibniz._, Scalaz._ /** The most general algebra, using both generalized and Elgot comonads as * well as a monad. @@ -30,12 +30,25 @@ final class GElgotAlgebraM[E[_], G[_], M[_], F[_], A](f: E[F[G[A]]] => M[A]) def attribute(implicit E: Comonad[E], G: Comonad[G], M: Functor[M], F: Functor[F]) = matryoshka.attribute[E, G, M, F, A](this) + def generalizeElgot[EE[_]: Comonad]( + implicit ev: GElgotAlgebraM[E, G, M, F, A] === GAlgebraM[G, M, F, A]): + GElgotAlgebraM[EE, G, M, F, A] = + matryoshka.generalizeW[EE, F[G[A]], M[A]](ev(this).apply) + + def generalize[GG[_]: Comonad](implicit EF: Functor[λ[α => E[F[α]]]], ev: GElgotAlgebraM[E, G, M, F, A] === ElgotAlgebraM[E, M, F, A]): + GElgotAlgebraM[E, GG, M, F, A] = + matryoshka.generalizeAlgebra[λ[α => E[F[α]]], GG, Id, M, F, A](ev(this).apply) + + def generalizeM[MM[_]: Applicative]( + implicit ev: GElgotAlgebraM[E, G, M, F, A] === GElgotAlgebra[E, G, F, A]): + GElgotAlgebraM[E, G, MM, F, A] = + matryoshka.generalizeM[MM, E[F[G[A]]], A](ev(this).apply) + def zip[B]( b: ⇒ GElgotAlgebraM[E, G, M, F, B])( implicit E: Functor[E], G: Functor[G], M: Applicative[M], F: Functor[F]): GElgotAlgebraM[E, G, M, F, (A, B)]= node => (this.f(node ∘ (_ ∘ (_ ∘ (_._1)))) ⊛ b(node ∘ (_ ∘ (_ ∘ (_._2)))))((_, _)) - } object GElgotAlgebraM { implicit def gElgotAlgebraMZip[E[_]: Functor, G[_]: Functor, M[_]: Applicative, F[_]: Functor]: @@ -51,6 +64,20 @@ object GElgotAlgebraM { sealed class GElgotCoalgebraM[E[_], G[_], M[_], F[_], A](f: A => M[E[F[G[A]]]]) extends Function1[A, M[E[F[G[A]]]]] { def apply(v1: A) = f(v1) + + def generalizeM[MM[_]: Applicative](implicit ev: GElgotCoalgebraM[E, G, M, F, A] === GElgotCoalgebra[E, G, F, A]): + GElgotCoalgebraM[E, G, MM, F, A] = + matryoshka.generalizeM[MM, A, E[F[G[A]]]](ev(this).apply) + + def generalizeElgot[EE[_]: Applicative]( + implicit M: Functor[M], + ev: GElgotCoalgebraM[E, G, M, F, A] === GCoalgebraM[G, M, F, A]): + GElgotCoalgebraM[EE, G, M, F, A] = + matryoshka.generalizeCoalgebra[M, EE, λ[α => F[G[α]]], A](ev(this).apply) + + def generalize[GG[_]: Applicative](implicit MEF: Functor[λ[α => M[E[F[α]]]]], ev: GElgotCoalgebraM[E, G, M, F, A] === ElgotCoalgebraM[E, M, F, A]): + GElgotCoalgebraM[E, GG, M, F, A] = + matryoshka.generalizeCoalgebra[λ[α => M[E[F[α]]]], GG, Id, A](ev(this).apply) } // NB: This class is needed to avoid the `Id[Id[_]]` “cyclic alias” issue. @@ -63,38 +90,9 @@ sealed trait ZeroIdInstances { implicit def toGElgotCoalgebraM[E[_], G[_], M[_], F[_], A](f: A => M[E[F[G[A]]]]): GElgotCoalgebraM[E, G, M, F, A] = new GElgotCoalgebraM[E, G, M, F, A](f) - } sealed trait OneIdInstances extends ZeroIdInstances { - implicit def toGElgotAlgebraOps[E[_], G[_], F[_], A]( - a: GElgotAlgebra[E, G, F, A]): - GElgotAlgebraOps[E, G, F, A] = - new GElgotAlgebraOps[E, G, F, A](a) - - implicit def toElgotAlgebraMOps[E[_], M[_], F[_], A]( - a: ElgotAlgebraM[E, M, F, A]): - ElgotAlgebraMOps[E, M, F, A] = - new ElgotAlgebraMOps[E, M, F, A](a) - - implicit def toGAlgebraMOps[G[_], M[_], F[_], A](a: GAlgebraM[G, M, F, A]): - GAlgebraMOps[G, M, F, A] = - new GAlgebraMOps[G, M, F, A](a) - - - implicit def toGElgotCoalgebraOps[E[_], G[_], F[_], A]( - a: GElgotCoalgebra[E, G, F, A]): - GElgotCoalgebraOps[E, G, F, A] = - new GElgotCoalgebraOps[E, G, F, A](a) - - implicit def toElgotCoalgebraMOps[E[_], M[_], F[_], A]( - a: ElgotCoalgebraM[E, M, F, A]): - ElgotCoalgebraMOps[E, M, F, A] = - new ElgotCoalgebraMOps[E, M, F, A](a) - - implicit def toGCoalgebraMOps[G[_], M[_], F[_], A](a: GCoalgebraM[G, M, F, A]): - GCoalgebraMOps[G, M, F, A] = - new GCoalgebraMOps[G, M, F, A](a) } sealed trait TwoIdInstances extends OneIdInstances { From e904b6e8f21b46a911cdb979a98ac53a3444ab68 Mon Sep 17 00:00:00 2001 From: Greg Pfeil Date: Wed, 16 Mar 2016 21:25:51 -0600 Subject: [PATCH 8/8] Make {Co}FreeOps `final` value classes. --- core/src/main/scala/matryoshka/package.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/matryoshka/package.scala b/core/src/main/scala/matryoshka/package.scala index 4803b440..894c663c 100644 --- a/core/src/main/scala/matryoshka/package.scala +++ b/core/src/main/scala/matryoshka/package.scala @@ -391,7 +391,8 @@ package object matryoshka CorecursiveOps[T, F] = new CorecursiveOps[T, F](f) - implicit sealed class CofreeOps[F[_], A](self: Cofree[F, A]) { + implicit final class CofreeOps[F[_], A](val self: Cofree[F, A]) + extends scala.AnyVal { def elgotCata[B](φ: ((A, F[B])) => B)(implicit F: Functor[F]): B = matryoshka.elgotCata(self)(φ) @@ -406,7 +407,8 @@ package object matryoshka matryoshka.cofCataM(self)(f) } - implicit sealed class FreeOps[F[_], A](self: Free[F, A]) { + implicit final class FreeOps[F[_], A](val self: Free[F, A]) + extends scala.AnyVal { def interpretCata(φ: F[A] => A)(implicit F: Functor[F]): A = matryoshka.interpretCata(self)(φ) }