Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/master' into compose
Browse files Browse the repository at this point in the history
  • Loading branch information
adelbertc committed Jun 3, 2016
2 parents e85a752 + 7f112dd commit cae7bd0
Show file tree
Hide file tree
Showing 124 changed files with 1,379 additions and 537 deletions.
26 changes: 16 additions & 10 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ lazy val commonJvmSettings = Seq(
// JVM settings. /~https://github.com/tkawachi/sbt-doctest/issues/52
) ++ catsDoctestSettings

lazy val catsSettings = buildSettings ++ commonSettings ++ publishSettings ++ scoverageSettings
lazy val catsSettings = buildSettings ++ commonSettings ++ publishSettings ++ scoverageSettings ++ javadocSettings

lazy val scalacheckVersion = "1.12.5"

Expand All @@ -95,21 +95,27 @@ lazy val testingDependencies = Seq(
libraryDependencies += "org.typelevel" %%% "catalysts-macros" % "0.0.2" % "test",
libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.0-M7" % "test")


/**
* Remove 2.10 projects from doc generation, as the macros used in the projects
* cause problems generating the documentation on scala 2.10. As the APIs for 2.10
* and 2.11 are the same this has no effect on the resultant documentation, though
* it does mean that the scaladocs cannot be generated when the build is in 2.10 mode.
*/
def noDocProjects(sv: String): Seq[ProjectReference] = CrossVersion.partialVersion(sv) match {
case Some((2, 10)) => Seq[ProjectReference](coreJVM)
case _ => Nil
* Remove 2.10 projects from doc generation, as the macros used in the projects
* cause problems generating the documentation on scala 2.10. As the APIs for 2.10
* and 2.11 are the same this has no effect on the resultant documentation, though
* it does mean that the scaladocs cannot be generated when the build is in 2.10 mode.
*/
def docsSourcesAndProjects(sv: String): (Boolean, Seq[ProjectReference]) =
CrossVersion.partialVersion(sv) match {
case Some((2, 10)) => (false, Nil)
case _ => (true, Seq(coreJVM, freeJVM))
}

lazy val javadocSettings = Seq(
sources in (Compile, doc) := (if (docsSourcesAndProjects(scalaVersion.value)._1) (sources in (Compile, doc)).value else Nil)
)

lazy val docSettings = Seq(
autoAPIMappings := true,
unidocProjectFilter in (ScalaUnidoc, unidoc) :=
inProjects(coreJVM, freeJVM) -- inProjects(noDocProjects(scalaVersion.value): _*),
inProjects(docsSourcesAndProjects(scalaVersion.value)._2:_*),
site.addMappingsToSiteDir(mappings in (ScalaUnidoc, packageDoc), "api"),
site.addMappingsToSiteDir(tut, "_tut"),
ghpagesNoJekyll := false,
Expand Down
40 changes: 38 additions & 2 deletions core/src/main/scala/cats/CoflatMap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,47 @@ package cats
import simulacrum.typeclass

/**
* Must obey the laws defined in cats.laws.CoflatMapLaws.
*/
* `CoflatMap` is the dual of `FlatMap`.
*
* Must obey the laws in cats.laws.CoflatMapLaws
*/
@typeclass trait CoflatMap[F[_]] extends Functor[F] {

/**
* `coflatMap` is the dual of `flatMap` on `FlatMap`. It applies
* a value in a Monadic context to a function that takes a value
* in a context and returns a normal value.
*
* Example:
* {{{
* scala> import cats.std.option._
* scala> import cats.Monad
* scala> import cats.CoflatMap
* scala> val fa = Monad[Option].pure(3)
* scala> def f(a: Option[Int]): Int = a match {
* | case Some(x) => 2 * x
* | case None => 0 }
* scala> CoflatMap[Option].coflatMap(fa)(f)
* res0: Option[Int] = Some(6)
* }}}
*/
def coflatMap[A, B](fa: F[A])(f: F[A] => B): F[B]

/**
* `coflatten` is the dual of `flatten` on `FlatMap`. Whereas flatten removes
* a layer of `F`, coflatten adds a layer of `F`
*
* Example:
* {{{
* scala> import cats.std.option._
* scala> import cats.Monad
* scala> import cats.CoflatMap
* scala> val fa = Monad[Option].pure(3)
* fa: Option[Int] = Some(3)
* scala> CoflatMap[Option].coflatten(fa)
* res0: Option[Option[Int]] = Some(Some(3))
* }}}
*/
def coflatten[A](fa: F[A]): F[F[A]] =
coflatMap(fa)(fa => fa)
}
24 changes: 22 additions & 2 deletions core/src/main/scala/cats/Comonad.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,29 @@ package cats

import simulacrum.typeclass


/**
* Must obey the laws defined in cats.laws.ComonadLaws.
*/
* Comonad
*
* Comonad is the dual of Monad. Whereas Monads allow for the composition of effectful functions,
* Comonads allow for composition of functions that extract the value from their context.
*
* Must obey the laws defined in cats.laws.ComonadLaws.
*/
@typeclass trait Comonad[F[_]] extends CoflatMap[F] {

/**
* `extract` is the dual of `pure` on Monad (via `Applicative`)
* and extracts the value from its context
*
* Example:
* {{{
* scala> import cats.Id
* scala> import cats.Comonad
* scala> val id: Id[Int] = 3
* scala> Comonad[Id].extract(id)
* res0: cats.Id[Int] = 3
* }}}
*/
def extract[A](x: F[A]): A
}
16 changes: 11 additions & 5 deletions core/src/main/scala/cats/Eval.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cats

import scala.annotation.tailrec
import cats.data.Xor
import cats.syntax.all._

/**
Expand Down Expand Up @@ -33,7 +34,7 @@ import cats.syntax.all._
* Eval instance -- this can defeat the trampolining and lead to stack
* overflows.
*/
sealed abstract class Eval[A] extends Serializable { self =>
sealed abstract class Eval[+A] extends Serializable { self =>

/**
* Evaluate the computation and return an A value.
Expand Down Expand Up @@ -279,7 +280,7 @@ object Eval extends EvalInstances {
cc.start().asInstanceOf[L],
cc.run.asInstanceOf[C] :: c.run.asInstanceOf[C] :: fs)
case xx =>
loop(c.run(xx.value).asInstanceOf[L], fs)
loop(c.run(xx.value), fs)
}
case x =>
fs match {
Expand All @@ -294,14 +295,19 @@ object Eval extends EvalInstances {

private[cats] trait EvalInstances extends EvalInstances0 {

implicit val evalBimonad: Bimonad[Eval] =
new Bimonad[Eval] {
implicit val evalBimonad: Bimonad[Eval] with MonadRec[Eval] =
new Bimonad[Eval] with MonadRec[Eval] {
override def map[A, B](fa: Eval[A])(f: A => B): Eval[B] = fa.map(f)
def pure[A](a: A): Eval[A] = Now(a)
override def pureEval[A](la: Eval[A]): Eval[A] = la
def flatMap[A, B](fa: Eval[A])(f: A => Eval[B]): Eval[B] = fa.flatMap(f)
def extract[A](la: Eval[A]): A = la.value
def coflatMap[A, B](fa: Eval[A])(f: Eval[A] => B): Eval[B] = Later(f(fa))
def tailRecM[A, B](a: A)(f: A => Eval[A Xor B]): Eval[B] =
f(a).flatMap(_ match {
case Xor.Left(a1) => tailRecM(a1)(f) // recursion OK here, since flatMap is lazy
case Xor.Right(b) => Eval.now(b)
})
}

implicit def evalOrder[A: Order]: Order[Eval[A]] =
Expand Down Expand Up @@ -350,7 +356,7 @@ trait EvalMonoid[A] extends Monoid[Eval[A]] with EvalSemigroup[A] {
trait EvalGroup[A] extends Group[Eval[A]] with EvalMonoid[A] {
implicit def algebra: Group[A]
def inverse(lx: Eval[A]): Eval[A] =
lx.map(_.inverse)
lx.map(_.inverse())
override def remove(lx: Eval[A], ly: Eval[A]): Eval[A] =
for { x <- lx; y <- ly } yield x |-| y
}
24 changes: 24 additions & 0 deletions core/src/main/scala/cats/FlatMapRec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package cats

import simulacrum.typeclass

import cats.data.Xor

/**
* Version of [[cats.FlatMap]] capable of stack-safe recursive `flatMap`s.
*
* Based on Phil Freeman's
* [[http://functorial.com/stack-safety-for-free/index.pdf Stack Safety for Free]].
*/
@typeclass trait FlatMapRec[F[_]] extends FlatMap[F] {

/**
* Keeps calling `f` until a `[[cats.data.Xor.Right Right]][B]` is returned.
*
* Implementations of this method must use constant stack space.
*
* `f` must use constant stack space. (It is OK to use a constant number of
* `map`s and `flatMap`s inside `f`.)
*/
def tailRecM[A, B](a: A)(f: A => F[A Xor B]): F[B]
}
10 changes: 9 additions & 1 deletion core/src/main/scala/cats/MonadError.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,15 @@ package cats
*
* This type class allows one to abstract over error-handling monads.
*/
trait MonadError[F[_], E] extends ApplicativeError[F, E] with Monad[F]
trait MonadError[F[_], E] extends ApplicativeError[F, E] with Monad[F] {

/**
* Turns a successful value into an error if it does not satisfy a given predicate.
*/
def ensure[A](fa: F[A])(error: => E)(predicate: A => Boolean): F[A] =
flatMap(fa)(a => if (predicate(a)) pure(a) else raiseError(error))

}

object MonadError {
def apply[F[_], E](implicit F: MonadError[F, E]): MonadError[F, E] = F
Expand Down
5 changes: 5 additions & 0 deletions core/src/main/scala/cats/MonadRec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package cats

import simulacrum.typeclass

@typeclass trait MonadRec[F[_]] extends Monad[F] with FlatMapRec[F]
31 changes: 31 additions & 0 deletions core/src/main/scala/cats/arrow/FunctionK.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cats
package arrow

import cats.data.{Xor, Coproduct}

trait FunctionK[F[_], G[_]] extends Serializable { self =>
def apply[A](fa: F[A]): G[A]

def compose[E[_]](f: FunctionK[E, F]): FunctionK[E, G] =
new FunctionK[E, G] {
def apply[A](fa: E[A]): G[A] = self.apply(f(fa))
}

def andThen[H[_]](f: FunctionK[G, H]): FunctionK[F, H] =
f.compose(self)

def or[H[_]](h: FunctionK[H,G]): FunctionK[Coproduct[F, H, ?], G] =
new FunctionK[Coproduct[F, H, ?], G] {
def apply[A](fa: Coproduct[F, H, A]): G[A] = fa.run match {
case Xor.Left(ff) => self(ff)
case Xor.Right(gg) => h(gg)
}
}
}

object FunctionK {
def id[F[_]]: FunctionK[F, F] =
new FunctionK[F, F] {
def apply[A](fa: F[A]): F[A] = fa
}
}
31 changes: 0 additions & 31 deletions core/src/main/scala/cats/arrow/NaturalTransformation.scala

This file was deleted.

12 changes: 6 additions & 6 deletions core/src/main/scala/cats/data/Cokleisli.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ object Cokleisli extends CokleisliInstances {
}

private[data] sealed abstract class CokleisliInstances extends CokleisliInstances0 {
implicit def cokleisliArrow[F[_]](implicit ev: Comonad[F]): Arrow[Cokleisli[F, ?, ?]] =
implicit def catsDataArrowForCokleisli[F[_]](implicit ev: Comonad[F]): Arrow[Cokleisli[F, ?, ?]] =
new CokleisliArrow[F] { def F: Comonad[F] = ev }

implicit def cokleisliMonad[F[_], A]: Monad[Cokleisli[F, A, ?]] = new Monad[Cokleisli[F, A, ?]] {
implicit def catsDataMonadForCokleisli[F[_], A]: Monad[Cokleisli[F, A, ?]] = new Monad[Cokleisli[F, A, ?]] {
def pure[B](x: B): Cokleisli[F, A, B] =
Cokleisli.pure(x)

Expand All @@ -58,18 +58,18 @@ private[data] sealed abstract class CokleisliInstances extends CokleisliInstance
fa.map(f)
}

implicit def cokleisliMonoidK[F[_]](implicit ev: Comonad[F]): MonoidK[Lambda[A => Cokleisli[F, A, A]]] =
implicit def catsDataMonoidKForCokleisli[F[_]](implicit ev: Comonad[F]): MonoidK[Lambda[A => Cokleisli[F, A, A]]] =
new CokleisliMonoidK[F] { def F: Comonad[F] = ev }
}

private[data] sealed abstract class CokleisliInstances0 {
implicit def cokleisliSplit[F[_]](implicit ev: CoflatMap[F]): Split[Cokleisli[F, ?, ?]] =
implicit def catsDataSplitForCokleisli[F[_]](implicit ev: CoflatMap[F]): Split[Cokleisli[F, ?, ?]] =
new CokleisliSplit[F] { def F: CoflatMap[F] = ev }

implicit def cokleisliProfunctor[F[_]](implicit ev: Functor[F]): Profunctor[Cokleisli[F, ?, ?]] =
implicit def catsDataProfunctorForCokleisli[F[_]](implicit ev: Functor[F]): Profunctor[Cokleisli[F, ?, ?]] =
new CokleisliProfunctor[F] { def F: Functor[F] = ev }

implicit def cokleisliSemigroupK[F[_]](implicit ev: CoflatMap[F]): SemigroupK[Lambda[A => Cokleisli[F, A, A]]] =
implicit def catsDataSemigroupKForCokleisli[F[_]](implicit ev: CoflatMap[F]): SemigroupK[Lambda[A => Cokleisli[F, A, A]]] =
new CokleisliSemigroupK[F] { def F: CoflatMap[F] = ev }
}

Expand Down
Loading

0 comments on commit cae7bd0

Please sign in to comment.