Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce cats-jvm and cats-js. #507

Merged
merged 8 commits into from
Sep 14, 2015
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
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
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
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)
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package cats
package jvm
package tests

import cats.data.Xor
import cats.laws.discipline._
import cats.jvm.std.future.{futureEq, futureComonad}
import cats.tests.CatsSuite

import scala.concurrent.{Await, ExecutionContext, Future}
import scala.concurrent.duration._
Expand Down
15 changes: 0 additions & 15 deletions laws/js/src/main/scala/cats/laws/Platform.scala

This file was deleted.

35 changes: 0 additions & 35 deletions laws/jvm/src/main/scala/cats/laws/Platform.scala

This file was deleted.

11 changes: 0 additions & 11 deletions laws/shared/src/main/scala/cats/laws/SerializableLaws.scala

This file was deleted.

52 changes: 52 additions & 0 deletions laws/src/main/scala/cats/laws/SerializableLaws.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package cats
package laws

import org.scalacheck.Prop
import org.scalacheck.Prop.{ False, Proof, Result }

import bricks.Platform

/**
* Check for Java Serializability.
*
* This laws is only applicative on the JVM, but is something we want
* to be sure to enforce. Therefore, we use bricks.Platform to do a
* runtime check rather than create a separate jvm-laws project.
*/
object SerializableLaws {

// This part is a bit tricky. Basically, we only want to test
// serializability on the JVM.
//
// The Platform.isJs macro will give us a literal true or false at
// compile time, so we rely on scalac to prune away the "other"
// branch. Thus, when scala.js look at this method it won't "see"
// the branch which was removed, and will avoid an error trying to
// suport java.io.*.
//
// This ends up being a lot nicer than having to split the entire
// laws project.

def serializable[A](a: A): Prop =
if (Platform.isJs) Prop(_ => Result(status = Proof)) else Prop { _ =>
import java.io.{ ByteArrayInputStream, ByteArrayOutputStream, ObjectInputStream, ObjectOutputStream }

val baos = new ByteArrayOutputStream()
val oos = new ObjectOutputStream(baos)
var ois: ObjectInputStream = null
try {
oos.writeObject(a)
oos.close()
val bais = new ByteArrayInputStream(baos.toByteArray())
ois = new ObjectInputStream(bais)
val a2 = ois.readObject()
ois.close()
Result(status = Proof)
} catch { case _: Throwable =>
Result(status = False)
} finally {
oos.close()
if (ois != null) ois.close()
}
}
}
2 changes: 1 addition & 1 deletion project/plugins.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ addSbtPlugin("pl.project13.scala"% "sbt-jmh" % "0.1.10")
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.6.0")
addSbtPlugin("org.scoverage" % "sbt-scoverage" % "1.2.0")
addSbtPlugin("com.typesafe.sbt" % "sbt-git" % "0.8.4")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.4")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.5")
Loading