Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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)
10 changes: 6 additions & 4 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 All @@ -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.
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