Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG/breaking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
- renamed `FunctorT#map` and `TraverseT#traverse` to `mapT` and `traverseT`
- added a bunch of new operations (many pulled from Quasar)
8 changes: 5 additions & 3 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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(
Expand All @@ -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"),
Expand Down
20 changes: 10 additions & 10 deletions core/shared/src/main/scala/matryoshka/Corecursive.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
Expand Down Expand Up @@ -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])
}
Expand Down
47 changes: 34 additions & 13 deletions core/shared/src/main/scala/matryoshka/FunctorT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)))
}
}
4 changes: 2 additions & 2 deletions core/shared/src/main/scala/matryoshka/Recursive.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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] =
Expand All @@ -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[_]] {
Expand Down
42 changes: 27 additions & 15 deletions core/shared/src/main/scala/matryoshka/TraverseT.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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])
Expand Down
6 changes: 3 additions & 3 deletions core/shared/src/main/scala/matryoshka/data/Mu.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand All @@ -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)))
})
}

Expand Down
7 changes: 0 additions & 7 deletions core/shared/src/main/scala/matryoshka/data/cofree.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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, ?]] =
Expand All @@ -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, ?]] {
Expand Down
8 changes: 0 additions & 8 deletions core/shared/src/main/scala/matryoshka/data/free.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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, ?]] {
Expand Down
12 changes: 7 additions & 5 deletions core/shared/src/main/scala/matryoshka/data/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
*
Expand Down
12 changes: 12 additions & 0 deletions core/shared/src/main/scala/matryoshka/implicits/IdOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]

Expand Down
Loading