Skip to content

Commit

Permalink
merge with master
Browse files Browse the repository at this point in the history
  • Loading branch information
julien-truffaut committed Sep 15, 2015
2 parents 4059746 + dee1d68 commit 01d807b
Show file tree
Hide file tree
Showing 114 changed files with 395 additions and 344 deletions.
54 changes: 38 additions & 16 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -92,26 +92,29 @@ lazy val catsJVM = project.in(file(".catsJVM"))
.settings(moduleName := "cats")
.settings(catsSettings)
.settings(commonJvmSettings)
.aggregate(macrosJVM, coreJVM, lawsJVM, freeJVM, stateJVM, testsJVM, docs, bench)
.dependsOn(macrosJVM, coreJVM, lawsJVM, freeJVM, stateJVM, testsJVM % "test-internal -> test", bench% "compile-internal;test-internal -> test")
.aggregate(macrosJVM, coreJVM, lawsJVM, freeJVM, stateJVM, testsJVM, jvm, docs, bench)
.dependsOn(macrosJVM, coreJVM, lawsJVM, freeJVM, stateJVM, testsJVM % "test-internal -> test", jvm, bench % "compile-internal;test-internal -> test")

lazy val catsJS = project.in(file(".catsJS"))
.settings(moduleName := "cats")
.settings(catsSettings)
.settings(commonJsSettings)
.aggregate(macrosJS, coreJS, lawsJS, freeJS, stateJS, testsJS)
.dependsOn(macrosJS, coreJS, lawsJS, freeJS, stateJS, testsJS % "test-internal -> test")
.aggregate(macrosJS, coreJS, lawsJS, freeJS, stateJS, testsJS, js)
.dependsOn(macrosJS, coreJS, lawsJS, freeJS, stateJS, testsJS % "test-internal -> test", js)
.enablePlugins(ScalaJSPlugin)


lazy val macros = crossProject.crossType(CrossType.Pure)
.settings(moduleName := "cats-macros")
.settings(catsSettings:_*)
.jsSettings(commonJsSettings:_*)
.jvmSettings(commonJvmSettings:_*)
.settings(scalacOptions := scalacOptions.value.filter(_ != "-Xfatal-warnings"))

lazy val macrosJVM = macros.jvm
lazy val macrosJVM = macros.jvm
lazy val macrosJS = macros.js


lazy val core = crossProject.crossType(CrossType.Pure)
.dependsOn(macros)
.settings(moduleName := "cats-core")
Expand All @@ -125,25 +128,20 @@ lazy val core = crossProject.crossType(CrossType.Pure)
lazy val coreJVM = core.jvm
lazy val coreJS = core.js

lazy val laws = crossProject
lazy val laws = crossProject.crossType(CrossType.Pure)
.dependsOn(macros, core)
.settings(moduleName := "cats-laws")
.settings(catsSettings:_*)
.settings(disciplineDependencies:_*)
.settings(libraryDependencies += "org.spire-math" %%% "algebra-laws" % "0.3.1")
.settings(libraryDependencies ++= Seq(
"org.spire-math" %%% "algebra-laws" % "0.3.1",
"com.github.inthenow" %%% "bricks-platform" % "0.0.1"))
.jsSettings(commonJsSettings:_*)
.jvmSettings(commonJvmSettings:_*)

lazy val lawsJVM = laws.jvm
lazy val lawsJS = laws.js

lazy val bench = project.dependsOn(macrosJVM, coreJVM, freeJVM, lawsJVM)
.settings(moduleName := "cats-bench")
.settings(catsSettings)
.settings(noPublishSettings)
.settings(jmhSettings)
.settings(commonJvmSettings)

lazy val free = crossProject.crossType(CrossType.Pure)
.dependsOn(macros, core, tests % "test-internal -> test")
.settings(moduleName := "cats-free")
Expand All @@ -164,19 +162,43 @@ lazy val state = crossProject.crossType(CrossType.Pure)
lazy val stateJVM = state.jvm
lazy val stateJS = state.js

lazy val tests = crossProject
lazy val tests = crossProject.crossType(CrossType.Pure)
.dependsOn(macros, core, laws)
.settings(moduleName := "cats-tests")
.settings(catsSettings:_*)
.settings(disciplineDependencies:_*)
.settings(noPublishSettings:_*)
.settings(libraryDependencies += "org.scalatest" %%% "scalatest" % "3.0.0-M7" % "test")
.settings(libraryDependencies ++= Seq(
"org.scalatest" %%% "scalatest" % "3.0.0-M7" % "test",
"com.github.inthenow" %%% "bricks-platform" % "0.0.1" % "test"))
.jsSettings(commonJsSettings:_*)
.jvmSettings(commonJvmSettings:_*)

lazy val testsJVM = tests.jvm
lazy val testsJS = tests.js

// cats-jvm is JVM-only
lazy val jvm = project
.dependsOn(macrosJVM, coreJVM, testsJVM % "test-internal -> test")
.settings(moduleName := "cats-jvm")
.settings(catsSettings:_*)
.settings(commonJvmSettings:_*)

// bench is currently JVM-only
lazy val bench = project.dependsOn(macrosJVM, coreJVM, freeJVM, lawsJVM)
.settings(moduleName := "cats-bench")
.settings(catsSettings)
.settings(noPublishSettings)
.settings(jmhSettings)
.settings(commonJvmSettings)

// cats-js is JS-only
lazy val js = project
.dependsOn(macrosJS, coreJS, testsJS % "test-internal -> test")
.settings(moduleName := "cats-js")
.settings(catsSettings:_*)
.settings(commonJsSettings:_*)

lazy val publishSettings = Seq(
homepage := Some(url("https://github.com/non/cats")),
licenses := Seq("MIT" -> url("http://opensource.org/licenses/MIT")),
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/scala/cats/data/Streaming.scala
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,8 @@ sealed abstract class Streaming[A] { lhs =>
if (n <= 0) Empty() else this match {
case Empty() => Empty()
case Wait(lt) => Wait(lt.map(_.take(n)))
case Cons(a, lt) => Cons(a, lt.map(_.take(n - 1)))
case Cons(a, lt) =>
Cons(a, if (n == 1) Now(Empty()) else lt.map(_.take(n - 1)))
}

/**
Expand Down
55 changes: 27 additions & 28 deletions core/src/main/scala/cats/std/future.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package cats
package std

import cats.syntax.eq._
import cats.syntax.all._

import scala.concurrent.{Await, ExecutionContext, Future}
import scala.concurrent.{ExecutionContext, Future}
import scala.concurrent.duration.FiniteDuration

trait FutureInstances extends FutureInstances1 {
Expand All @@ -23,39 +23,38 @@ trait FutureInstances extends FutureInstances1 {
override def map[A, B](fa: Future[A])(f: A => B): Future[B] = fa.map(f)
}

implicit def futureSemigroup[A](implicit A: Semigroup[A], ec: ExecutionContext): Semigroup[Future[A]] =
new FutureSemigroup[A]()

def futureEq[A](atMost: FiniteDuration)(implicit A: Eq[A], ec: ExecutionContext): Eq[Future[A]] =
new Eq[Future[A]] {
def eqv(fx: Future[A], fy: Future[A]): Boolean =
Await.result((fx zip fy).map { case (x, y) => x === y }, atMost)
}
implicit def futureGroup[A: Group](implicit ec: ExecutionContext): Group[Future[A]] =
new FutureGroup[A]
}

trait FutureInstances1 {

def futureComonad(atMost: FiniteDuration)(implicit ec: ExecutionContext): Comonad[Future] =
new FutureCoflatMap with Comonad[Future] {

def extract[A](x: Future[A]): A = Await.result(x, atMost)

def map[A, B](fa: Future[A])(f: A => B): Future[B] = fa.map(f)
}

implicit def futureMonoid[A](implicit A: Monoid[A], ec: ExecutionContext): Monoid[Future[A]] =
new FutureSemigroup[A] with Monoid[Future[A]] {

def empty: Future[A] = Future.successful(A.empty)
}
trait FutureInstances1 extends FutureInstances2 {
implicit def futureMonoid[A: Monoid](implicit ec: ExecutionContext): Monoid[Future[A]] =
new FutureMonoid[A]
}

private[std] abstract class FutureCoflatMap(implicit ec: ExecutionContext) extends CoflatMap[Future] {
trait FutureInstances2 {
implicit def futureSemigroup[A: Semigroup](implicit ec: ExecutionContext): Semigroup[Future[A]] =
new FutureSemigroup[A]
}

private[cats] abstract class FutureCoflatMap(implicit ec: ExecutionContext) extends CoflatMap[Future] {
def map[A, B](fa: Future[A])(f: A => B): Future[B] = fa.map(f)
def coflatMap[A, B](fa: Future[A])(f: Future[A] => B): Future[B] = Future(f(fa))
}

private[std] class FutureSemigroup[A](implicit A: Semigroup[A], ec: ExecutionContext) extends Semigroup[Future[A]] {
private[cats] class FutureSemigroup[A: Semigroup](implicit ec: ExecutionContext) extends Semigroup[Future[A]] {
def combine(fx: Future[A], fy: Future[A]): Future[A] =
(fx zip fy).map { case (x, y) => x |+| y }
}

private[cats] class FutureMonoid[A](implicit A: Monoid[A], ec: ExecutionContext) extends FutureSemigroup[A] with Monoid[Future[A]] {
def empty: Future[A] =
Future.successful(A.empty)
}

def combine(fx: Future[A], fy: Future[A]): Future[A] = (fx zip fy).map((A.combine _).tupled)
private[cats] class FutureGroup[A](implicit A: Group[A], ec: ExecutionContext) extends FutureMonoid[A] with Group[Future[A]] {
def inverse(fx: Future[A]): Future[A] =
fx.map(_.inverse)
override def remove(fx: Future[A], fy: Future[A]): Future[A] =
(fx zip fy).map { case (x, y) => x |-| y }
}
1 change: 1 addition & 0 deletions core/src/main/scala/cats/syntax/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ package object syntax {
object flatMap extends FlatMapSyntax
object foldable extends FoldableSyntax
object functor extends FunctorSyntax
object group extends GroupSyntax
object invariant extends InvariantSyntax
object monadCombine extends MonadCombineSyntax
object monadFilter extends MonadFilterSyntax
Expand Down
16 changes: 0 additions & 16 deletions free/src/main/scala/cats/free/Coyoneda.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,6 @@ object Coyoneda {
/** `F[A]` converts to `Coyoneda[F,A]` for any `F` */
def lift[F[_], A](fa: F[A]): Coyoneda[F, A] = apply(fa)(identity[A])

/**
* Represents a partially-built Coyoneda instance. Used in the `by` method.
*/
final class By[F[_]] {
def apply[A, B](k: A => B)(implicit F: F[A]): Aux[F, B, A] = Coyoneda(F)(k)
}

/**
* Partial application of type parameters to `apply`.
*
* It can be nicer to say `Coyoneda.by[F]{ x: X => ... }`
*
* ...instead of `Coyoneda[...](...){ x => ... }`.
*/
def by[F[_]]: By[F] = new By[F]

/** Like `lift(fa).map(_k)`. */
def apply[F[_], A, B](fa: F[A])(k0: A => B): Aux[F, B, A] =
new Coyoneda[F, B] {
Expand Down
10 changes: 2 additions & 8 deletions free/src/main/scala/cats/free/FreeApplicative.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,6 @@ sealed abstract class FreeApplicative[F[_], A] extends Product with Serializable
case x@Ap() => apply(x.pivot)(x.fn.map(f compose _))
}

/** Natural Transformation of FreeApplicative based on given Natural Transformation */
final def hoist[G[_]](f: F ~> G): FA[G, A] =
this match {
case Pure(a) => Pure[G, A](a)
case x@Ap() => apply(f(x.pivot))(x.fn.hoist(f))
}

/** Interprets/Runs the sequence of operations using the semantics of Applicative G
* Tail recursive only if G provides tail recursive interpretation (ie G is FreeMonad)
*/
Expand All @@ -45,7 +38,8 @@ sealed abstract class FreeApplicative[F[_], A] extends Product with Serializable
final def fold(implicit F: Applicative[F]): F[A] =
foldMap(NaturalTransformation.id[F])

final def compile[G[_]](f: F ~> G)(implicit G: Applicative[G]): FA[G, A] =
/** Interpret this algebra into another FreeApplicative */
final def compile[G[_]](f: F ~> G): FA[G, A] =
foldMap[FA[G, ?]] {
new NaturalTransformation[F, FA[G, ?]] {
def apply[B](fa: F[B]): FA[G, B] = lift(f(fa))
Expand Down
43 changes: 43 additions & 0 deletions free/src/test/scala/cats/free/CoyonedaTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package cats
package free

import cats.arrow.NaturalTransformation
import cats.tests.CatsSuite
import cats.laws.discipline.{ArbitraryK, FunctorTests, SerializableTests}

import org.scalacheck.{Arbitrary, Gen}
import org.scalacheck.Arbitrary.arbitrary
import org.scalacheck.Prop.forAll

class CoyonedaTests extends CatsSuite {
implicit def coyonedaArbitraryK[F[_] : Functor](implicit F: ArbitraryK[F]): ArbitraryK[Coyoneda[F, ?]] =
new ArbitraryK[Coyoneda[F, ?]]{
def synthesize[A: Arbitrary]: Arbitrary[Coyoneda[F, A]] = coyonedaArbitrary[F, A]
}

implicit def coyonedaArbitrary[F[_] : Functor, A : Arbitrary](implicit F: ArbitraryK[F]): Arbitrary[Coyoneda[F, A]] =
Arbitrary(F.synthesize[A].arbitrary.map(Coyoneda.lift))

implicit def coyonedaEq[F[_]: Functor, A](implicit FA: Eq[F[A]]): Eq[Coyoneda[F, A]] =
new Eq[Coyoneda[F, A]] {
def eqv(a: Coyoneda[F, A], b: Coyoneda[F, A]): Boolean = FA.eqv(a.run, b.run)
}

checkAll("Coyoneda[Option, ?]", FunctorTests[Coyoneda[Option, ?]].functor[Int, Int, Int])
checkAll("Functor[Coyoneda[Option, ?]]", SerializableTests.serializable(Functor[Coyoneda[Option, ?]]))

test("toYoneda and then toCoyoneda is identity")(check {
// Issues inferring syntax for Eq, using instance explicitly
forAll((y: Coyoneda[Option, Int]) => coyonedaEq[Option, Int].eqv(y.toYoneda.toCoyoneda, y))
})

test("transform and run is same as applying natural trans") {
val nt =
new NaturalTransformation[Option, List] {
def apply[A](fa: Option[A]): List[A] = fa.toList
}
val o = Option("hello")
val c = Coyoneda.lift(o)
c.transform(nt).run should === (nt(o))
}
}
6 changes: 3 additions & 3 deletions free/src/test/scala/cats/free/FreeApplicativeTests.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class FreeApplicativeTests extends CatsSuite {
val y = FreeApplicative.pure[Option, Int](n)
val f = x.map(i => (j: Int) => i + j)
val r = y.ap(f)
assert(r.fold == Apply[Option].map2(o1, o2)(_ + _))
r.fold should === (Apply[Option].map2(o1, o2)(_ + _))
}

test("FreeApplicative#compile") {
Expand All @@ -50,7 +50,7 @@ class FreeApplicativeTests extends CatsSuite {
val nt = NaturalTransformation.id[Id]
val r1 = y.ap(f)
val r2 = r1.compile(nt)
assert(r1.foldMap(nt) == r2.foldMap(nt))
r1.foldMap(nt) should === (r2.foldMap(nt))
}

test("FreeApplicative#monad") {
Expand All @@ -63,6 +63,6 @@ class FreeApplicativeTests extends CatsSuite {
new NaturalTransformation[Id, Id] {
def apply[A](fa: Id[A]): Id[A] = fa
}
assert(r1.foldMap(nt) == r2.foldMap(nt))
r1.foldMap(nt) should === (r2.foldMap(nt))
}
}
30 changes: 30 additions & 0 deletions free/src/test/scala/cats/free/YonedaTests.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package cats
package free

import cats.tests.CatsSuite
import cats.laws.discipline.{ArbitraryK, FunctorTests, SerializableTests}

import org.scalacheck.Arbitrary
import org.scalacheck.Prop.forAll

class YonedaTests extends CatsSuite {
implicit def yonedaArbitraryK[F[_] : Functor](implicit F: ArbitraryK[F]): ArbitraryK[Yoneda[F, ?]] =
new ArbitraryK[Yoneda[F, ?]]{
def synthesize[A: Arbitrary]: Arbitrary[Yoneda[F, A]] =
Arbitrary(F.synthesize[A].arbitrary.map(Yoneda(_)))
}

implicit def yonedaArbitrary[F[_] : Functor : ArbitraryK, A : Arbitrary]: Arbitrary[Yoneda[F, A]] = yonedaArbitraryK[F].synthesize[A]

implicit def yonedaEq[F[_]: Functor, A](implicit FA: Eq[F[A]]): Eq[Yoneda[F, A]] =
new Eq[Yoneda[F, A]] {
def eqv(a: Yoneda[F, A], b: Yoneda[F, A]): Boolean = FA.eqv(a.run, b.run)
}

checkAll("Yoneda[Option, ?]", FunctorTests[Yoneda[Option, ?]].functor[Int, Int, Int])
checkAll("Functor[Yoneda[Option, ?]]", SerializableTests.serializable(Functor[Yoneda[Option, ?]]))

test("toCoyoneda and then toYoneda is identity")(check {
forAll((y: Yoneda[Option, Int]) => y.toCoyoneda.toYoneda === y)
})
}
43 changes: 43 additions & 0 deletions jvm/src/main/scala/cats/jvm/std/future.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package cats
package jvm
package std

import scala.concurrent.{Await, Future}
import scala.concurrent.{ExecutionContext => E}
import scala.concurrent.duration.FiniteDuration

import cats.std.FutureCoflatMap
import cats.syntax.all._

object future extends FutureInstances0

trait FutureInstances0 extends FutureInstances1 {
def futureComonad(atMost: FiniteDuration)(implicit ec: E): Comonad[Future] =
new FutureCoflatMap with Comonad[Future] {
def extract[A](x: Future[A]): A =
Await.result(x, atMost)
}

def futureOrder[A: Order](atMost: FiniteDuration)(implicit ec: E): Eq[Future[A]] =
new Order[Future[A]] {
def compare(x: Future[A], y: Future[A]): Int =
Await.result((x zip y).map { case (x, y) => x compare y }, atMost)
}
}

trait FutureInstances1 extends FutureInstances2 {
def futurePartialOrder[A: PartialOrder](atMost: FiniteDuration)(implicit ec: E): PartialOrder[Future[A]] =
new PartialOrder[Future[A]] {
def partialCompare(x: Future[A], y: Future[A]): Double =
Await.result((x zip y).map { case (x, y) => x partialCompare y }, atMost)
}

}

trait FutureInstances2 {
def futureEq[A: Eq](atMost: FiniteDuration)(implicit ec: E): Eq[Future[A]] =
new Eq[Future[A]] {
def eqv(x: Future[A], y: Future[A]): Boolean =
Await.result((x zip y).map { case (x, y) => x === y }, atMost)
}
}
Loading

0 comments on commit 01d807b

Please sign in to comment.