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

Proof of concept creating a cats-kernel module for Cats/Algebra. #786

Closed
wants to merge 2 commits into from
Closed
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: 1 addition & 1 deletion bench/src/main/scala/cats/bench/FoldBench.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package cats.bench

import algebra.std.string._
import cats.std.string._
import cats.data.Const
import cats.std.list._
import cats.{Foldable, Traverse}
Expand Down
39 changes: 24 additions & 15 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,18 @@ lazy val catsDoctestSettings = Seq(
) ++ doctestSettings

lazy val commonSettings = Seq(
scalacOptions ++= commonScalacOptions,
scalacOptions ++= commonScalacOptions ++
(CrossVersion.partialVersion(scalaVersion.value) match {
case Some((2, 10)) => Seq.empty
case _ => Seq("-Ywarn-value-discard")
}),
resolvers ++= Seq(
"bintray/non" at "http://dl.bintray.com/non/maven",
Resolver.sonatypeRepo("releases"),
Resolver.sonatypeRepo("snapshots")
),
libraryDependencies ++= Seq(
"com.github.mpilquist" %%% "simulacrum" % "0.5.0",
"org.spire-math" %%% "algebra" % "0.3.1",
"org.spire-math" %%% "algebra-std" % "0.3.1",
"org.typelevel" %%% "machinist" % "0.4.1",
compilerPlugin("org.scalamacros" %% "paradise" % "2.1.0-M5" cross CrossVersion.full),
compilerPlugin("org.spire-math" %% "kind-projector" % "0.6.3")
Expand Down Expand Up @@ -100,15 +102,15 @@ lazy val catsJVM = project.in(file(".catsJVM"))
.settings(moduleName := "cats")
.settings(catsSettings)
.settings(commonJvmSettings)
.aggregate(macrosJVM, coreJVM, lawsJVM, testsJVM, jvm, docs, bench)
.dependsOn(macrosJVM, coreJVM, lawsJVM, testsJVM % "test-internal -> test", jvm, bench % "compile-internal;test-internal -> test")
.aggregate(macrosJVM, kernelJVM, coreJVM, lawsJVM, testsJVM, jvm, docs, bench)
.dependsOn(macrosJVM, kernelJVM, coreJVM, lawsJVM, 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, testsJS, js)
.dependsOn(macrosJS, coreJS, lawsJS, testsJS % "test-internal -> test", js)
.aggregate(macrosJS, kernelJS, coreJS, lawsJS, testsJS, js)
.dependsOn(macrosJS, kernelJS, coreJS, lawsJS, testsJS % "test-internal -> test", js)
.enablePlugins(ScalaJSPlugin)


Expand All @@ -123,8 +125,17 @@ lazy val macrosJVM = macros.jvm
lazy val macrosJS = macros.js


lazy val kernel = crossProject.crossType(CrossType.Pure)
.settings(moduleName := "cats-kernel")
.settings(catsSettings:_*)
.jsSettings(commonJsSettings:_*)
.jvmSettings(commonJvmSettings:_*)

lazy val kernelJVM = kernel.jvm
lazy val kernelJS = kernel.js

lazy val core = crossProject.crossType(CrossType.Pure)
.dependsOn(macros)
.dependsOn(macros, kernel)
.settings(moduleName := "cats-core")
.settings(catsSettings:_*)
.settings(
Expand All @@ -138,12 +149,11 @@ lazy val coreJVM = core.jvm
lazy val coreJS = core.js

lazy val laws = crossProject.crossType(CrossType.Pure)
.dependsOn(macros, core)
.dependsOn(macros, kernel, core)
.settings(moduleName := "cats-laws")
.settings(catsSettings:_*)
.settings(disciplineDependencies:_*)
.settings(libraryDependencies ++= Seq(
"org.spire-math" %%% "algebra-laws" % "0.3.1",
"org.typelevel" %%% "catalysts-platform" % "0.0.2"))
.jsSettings(commonJsSettings:_*)
.jvmSettings(commonJvmSettings:_*)
Expand All @@ -152,7 +162,7 @@ lazy val lawsJVM = laws.jvm
lazy val lawsJS = laws.js

lazy val tests = crossProject.crossType(CrossType.Pure)
.dependsOn(macros, core, laws)
.dependsOn(macros, kernel, core, laws)
.settings(moduleName := "cats-tests")
.settings(catsSettings:_*)
.settings(disciplineDependencies:_*)
Expand All @@ -168,13 +178,13 @@ lazy val testsJS = tests.js

// cats-jvm is JVM-only
lazy val jvm = project
.dependsOn(macrosJVM, coreJVM, testsJVM % "test-internal -> test")
.dependsOn(macrosJVM, kernelJVM, 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, lawsJVM)
lazy val bench = project.dependsOn(macrosJVM, kernelJVM, coreJVM, lawsJVM)
.settings(moduleName := "cats-bench")
.settings(catsSettings)
.settings(noPublishSettings)
Expand All @@ -183,7 +193,7 @@ lazy val bench = project.dependsOn(macrosJVM, coreJVM, lawsJVM)

// cats-js is JS-only
lazy val js = project
.dependsOn(macrosJS, coreJS, testsJS % "test-internal -> test")
.dependsOn(macrosJS, kernelJS, coreJS, testsJS % "test-internal -> test")
.settings(moduleName := "cats-js")
.settings(catsSettings:_*)
.settings(commonJsSettings:_*)
Expand Down Expand Up @@ -269,7 +279,6 @@ lazy val commonScalacOptions = Seq(
"-Yno-adapted-args",
"-Ywarn-dead-code",
"-Ywarn-numeric-widen",
"-Ywarn-value-discard",
"-Xfuture"
)

Expand Down
14 changes: 0 additions & 14 deletions core/src/main/scala/cats/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,4 @@ package object cats {
def traverse[G[_], A, B](a: A)(f: A => G[B])(implicit G: Applicative[G]): G[B] =
f(a)
}

type Eq[A] = algebra.Eq[A]
type PartialOrder[A] = algebra.PartialOrder[A]
type Order[A] = algebra.Order[A]
type Semigroup[A] = algebra.Semigroup[A]
type Monoid[A] = algebra.Monoid[A]
type Group[A] = algebra.Group[A]

val Eq = algebra.Eq
val PartialOrder = algebra.PartialOrder
val Order = algebra.Order
val Semigroup = algebra.Semigroup
val Monoid = algebra.Monoid
val Group = algebra.Group
}
85 changes: 37 additions & 48 deletions core/src/main/scala/cats/std/anyval.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package cats
package std

import algebra.CommutativeGroup
import algebra.ring.AdditiveCommutativeGroup

trait AnyValInstances
extends IntInstances
with ByteInstances
Expand All @@ -15,33 +12,35 @@ trait AnyValInstances
with BooleanInstances
with UnitInstances

trait IntInstances extends algebra.std.IntInstances {
trait IntInstances {

implicit val intShow: Show[Int] =
Show.fromToString[Int]

implicit val intGroup: CommutativeGroup[Int] =
AdditiveCommutativeGroup[Int].additive
implicit val intAlgebra: Group[Int] with Order[Int] =
new Group[Int] with Order[Int] {
def empty: Int = 0
def inverse(a: Int): Int = -a
def combine(x: Int, y: Int): Int = x+y
def compare(x: Int, y: Int): Int =
if (x < y) -1 else if (x > y) 1 else 0
}

}

trait ByteInstances /* missing algebra type classes */ {
trait ByteInstances {

implicit val byteShow: Show[Byte] =
Show.fromToString[Byte]

// TODO: replace this minimal algebra with one from the algebra project
implicit val byteAlgebra: CommutativeGroup[Byte] with Order[Byte] =
new CommutativeGroup[Byte] with Order[Byte] {
def combine(x: Byte, y: Byte): Byte = (x + y).toByte
def empty: Byte = 0
def inverse(x: Byte): Byte = (-x).toByte
implicit val byteOrder: Order[Byte] =
new Order[Byte] {
def compare(x: Byte, y: Byte): Int =
if (x < y) -1 else if (y < x) 1 else 0
}
}

trait CharInstances /* missing algebra type classes */ {
trait CharInstances {

implicit val charShow: Show[Char] =
Show.fromToString[Char]
Expand All @@ -53,90 +52,80 @@ trait CharInstances /* missing algebra type classes */ {
}
}

trait ShortInstances /* missing algebra type classes */ {
trait ShortInstances {

implicit val shortShow: Show[Short] =
Show.fromToString[Short]

// TODO: replace this minimal algebra with one from the algebra project
implicit val shortAlgebra: CommutativeGroup[Short] with Order[Short] =
new CommutativeGroup[Short] with Order[Short] {
def combine(x: Short, y: Short): Short = (x + y).toShort
def empty: Short = 0
def inverse(x: Short): Short = (-x).toShort
implicit val shortOrder: Order[Short] =
new Order[Short] {
def compare(x: Short, y: Short): Int =
if (x < y) -1 else if (y < x) 1 else 0
}

}

trait LongInstances /* missing algebra type classes */ {
trait LongInstances {

implicit val longShow: Show[Long] =
Show.fromToString[Long]

// TODO: replace this minimal algebra with one from the algebra project
implicit val longAlgebra: CommutativeGroup[Long] with Order[Long] =
new CommutativeGroup[Long] with Order[Long] {
def combine(x: Long, y: Long): Long = x + y
implicit val longAlgebra: Group[Long] with Order[Long] =
new Group[Long] with Order[Long] {
def empty: Long = 0L
def inverse(x: Long): Long = -x
def inverse(a: Long): Long = -a
def combine(x: Long, y: Long): Long = x+y
def compare(x: Long, y: Long): Int =
if (x < y) -1 else if (y < x) 1 else 0
}
}

trait FloatInstances /* missing algebra type classes */ {
trait FloatInstances {

implicit val floatShow: Show[Float] =
Show.fromToString[Float]

// TODO: replace this minimal algebra with one from the algebra project
implicit val floatAlgebra: CommutativeGroup[Float] with Order[Float] =
new CommutativeGroup[Float] with Order[Float] {
def combine(x: Float, y: Float): Float = x + y
def empty: Float = 0F
def inverse(x: Float): Float = -x
implicit val floatOrder: Order[Float] =
new Order[Float] {
def compare(x: Float, y: Float): Int =
java.lang.Float.compare(x, y)
}

}

trait DoubleInstances /* missing algebra type classes */ {
trait DoubleInstances {

implicit val doubleShow: Show[Double] =
Show.fromToString[Double]

// TODO: replace this minimal algebra with one from the algebra project
implicit val doubleAlgebra: CommutativeGroup[Double] with Order[Double] =
new CommutativeGroup[Double] with Order[Double] {
def combine(x: Double, y: Double): Double = x + y
def empty: Double = 0D
def inverse(x: Double): Double = -x
implicit val doubleOrder: Order[Double] =
new Order[Double] {
def compare(x: Double, y: Double): Int =
java.lang.Double.compare(x, y)
}

}

trait BooleanInstances extends algebra.std.BooleanInstances {
trait BooleanInstances {

implicit val booleanShow: Show[Boolean] =
Show.fromToString[Boolean]

implicit val booleanOrder: Order[Boolean] =
new Order[Boolean] {
def compare(x: Boolean, y: Boolean): Int =
if (x == y) 0 else if (x) 1 else -1
}

}

trait UnitInstances /* missing algebra type classes */ {
trait UnitInstances {

implicit val unitShow: Show[Unit] =
Show.fromToString[Unit]

implicit val unitAlgebra: CommutativeGroup[Unit] with Order[Unit] =
new CommutativeGroup[Unit] with Order[Unit] {
def combine(x: Unit, y: Unit): Unit = ()
def empty: Unit = ()
def inverse(x: Unit): Unit = ()
implicit val unitOrder: Order[Unit] =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the group here can be useful no? (Allows you do use some Monads that want Monoids on some values, e.g. Writer?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean plain Group rather than CommutativeGroup? Yes, agreed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sorry. I overlooked that you just removed the CommutativeGroup, but that's another issue to keep in mind with standard instances: many standard ones are commutative, but kernel won't have it, so something for algebra to keep in mind.

new Order[Unit] {
def compare(x: Unit, y: Unit): Int = 0
}

Expand Down
9 changes: 8 additions & 1 deletion core/src/main/scala/cats/std/bigInt.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
package cats
package std

trait BigIntInstances extends algebra.std.BigIntInstances {
trait BigIntInstances {
implicit val bigIntAlgebra: BigIntAlgebra =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is out of the scope of this PR, but just to jot it down while I'm thinking of it: is there any benefit to having this statically typed to BitIntAlgebra instead of just Order[BigInt]?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are you proposing as an alternative?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was just proposing : Order[BigInt] instead of : BigIntAlgebra for the return type. As far as I know that's the approach we are taking pretty much everywhere else.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, why not the Group[BigInt] here? We have it for Int, Long?

new BigIntAlgebra

implicit val bigIntShow: Show[BigInt] =
Show.fromToString[BigInt]
}

class BigIntAlgebra extends Order[BigInt] {
def compare(x: BigInt, y: BigInt): Int = x compare y
}
1 change: 0 additions & 1 deletion core/src/main/scala/cats/std/function.scala
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package cats
package std

import algebra.Eq
import cats.arrow.{Arrow, Choice}
import cats.data.Xor
import cats.functor.Contravariant
Expand Down
42 changes: 39 additions & 3 deletions core/src/main/scala/cats/std/list.scala
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
package cats
package std

import algebra.Eq
import algebra.std.{ListMonoid, ListOrder}

import cats.data.Streaming
import cats.syntax.order._
import cats.syntax.show._
Expand Down Expand Up @@ -120,3 +117,42 @@ private[std] sealed trait ListInstances2 {
}
}
}

class ListOrder[A](implicit ev: Order[A]) extends Order[List[A]] {
def compare(xs: List[A], ys: List[A]): Int = {
@tailrec def loop(xs: List[A], ys: List[A]): Int =
xs match {
case Nil =>
if (ys.isEmpty) 0 else -1
case x :: xs =>
ys match {
case Nil => 1
case y :: ys =>
val n = ev.compare(x, y)
if (n != 0) n else loop(xs, ys)
}
}
loop(xs, ys)
}
}

class ListMonoid[A] extends Monoid[List[A]] {
def empty: List[A] = Nil
def combine(x: List[A], y: List[A]): List[A] = x ::: y

override def combineN(x: List[A], n: Int): List[A] = {
val buf = ListBuffer.empty[A]
@tailrec def loop(i: Int): List[A] =
if (i <= 0) buf.toList else {
buf ++= x
loop(i - 1)
}
loop(n)
}

override def combineAll(xs: TraversableOnce[List[A]]): List[A] = {
val buf = ListBuffer.empty[A]
xs.foreach(buf ++= _)
buf.toList
}
}
Loading