Skip to content

Commit

Permalink
Set up Machinist, use macros for Eq through Order syntax.
Browse files Browse the repository at this point in the history
This commit adds cats-macros, upon which cats depends.
Right now, the only macros being used are Machinist's
operator macros, which are configured in cats.macros.Ops.

The punchline of doing this is that:

    def xyz[A: Eq](x: A, y: A): Boolean =
      x === y

is now exactly the same as:

    def xyz[A](x: A, y: A)(implicit A: Eq[A]): Boolean =
      A.eqv(x, y)

i.e. the syntax implicit conversions are completely erased.
This eliminates the performance penalty that syntax normally
implies. It also means the syntax classes don't need to be
value classes (or specialized) since they are not used at
runtime.
  • Loading branch information
non committed Feb 5, 2015
1 parent f2a007b commit ce14308
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 30 deletions.
20 changes: 12 additions & 8 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,18 @@ lazy val aggregate = project.in(file("."))
.settings(catsSettings: _*)
.settings(docSettings: _*)
.settings(noPublishSettings: _*)
.aggregate(core, laws, tests, data, std, examples)
.dependsOn(core, laws, tests, data, std, examples)
.aggregate(macros, core, laws, tests, data, std, examples)
.dependsOn(macros, core, laws, tests, data, std, examples)

lazy val core = project
lazy val macros = project
.settings(moduleName := "cats-macros")
.settings(catsSettings: _*)

lazy val core = project.dependsOn(macros)
.settings(moduleName := "cats")
.settings(catsSettings: _*)

lazy val laws = project.dependsOn(core, data)
lazy val laws = project.dependsOn(macros, core, data)
.settings(moduleName := "cats-laws")
.settings(catsSettings: _*)
.settings(
Expand All @@ -72,14 +76,14 @@ lazy val laws = project.dependsOn(core, data)
)
)

lazy val std = project.dependsOn(core, laws)
lazy val std = project.dependsOn(macros, core, laws)
.settings(moduleName := "cats-std")
.settings(catsSettings: _*)
.settings(
libraryDependencies += "org.spire-math" %% "algebra-std" % "0.2.0-SNAPSHOT" from "http://plastic-idolatry.com/jars/algebra-std_2.11-0.2.0-SNAPSHOT.jar"
)

lazy val tests = project.dependsOn(core, data, std, laws)
lazy val tests = project.dependsOn(macros, core, data, std, laws)
.settings(moduleName := "cats-tests")
.settings(catsSettings: _*)
.settings(noPublishSettings: _*)
Expand All @@ -89,11 +93,11 @@ lazy val tests = project.dependsOn(core, data, std, laws)
)
)

lazy val data = project.dependsOn(core)
lazy val data = project.dependsOn(macros, core)
.settings(moduleName := "cats-data")
.settings(catsSettings: _*)

lazy val examples = project.dependsOn(core)
lazy val examples = project.dependsOn(macros, core)
.settings(moduleName := "cats-examples")
.settings(catsSettings: _*)
.settings(noPublishSettings: _*)
Expand Down
11 changes: 6 additions & 5 deletions core/src/main/scala/cats/syntax/eq.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
package cats
package syntax

import cats.macros.Ops
import scala.language.experimental.macros

This comment has been minimized.

Copy link
@julien-truffaut

julien-truffaut Feb 5, 2015

Contributor

we could add this to the sbt file:

scalacOptions  += "-language:experimental.macros",

This comment has been minimized.

Copy link
@non

non Feb 5, 2015

Author Contributor

Oh, that's a good idea, to eliminate some noise.


trait EqSyntax {
// TODO: use simulacrum instances eventually
implicit def eqSyntax[A: Eq](a: A) =
new EqOps[A](a)
implicit def eqSyntax[A: Eq](a: A) = new EqOps[A](a)
}

class EqOps[A](lhs: A)(implicit A: Eq[A]) {
def ===(rhs: A): Boolean = A.eqv(lhs, rhs)
def =!=(rhs: A): Boolean = A.neqv(lhs, rhs)
def ===(rhs: A): Boolean = macro Ops.binop[A, Boolean]
def =!=(rhs: A): Boolean = macro Ops.binop[A, Boolean]
}
13 changes: 7 additions & 6 deletions core/src/main/scala/cats/syntax/order.scala
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
package cats
package syntax

import cats.macros.Ops
import scala.language.experimental.macros

trait OrderSyntax {
// TODO: use simulacrum instances eventually
implicit def orderSyntax[A: Order](a: A) =
new OrderOps[A](a)
implicit def orderSyntax[A: Order](a: A) = new OrderOps[A](a)
}

class OrderOps[A](lhs: A)(implicit A: Order[A]) {
def compare(rhs: A): Int = A.compare(lhs, rhs)
def min(rhs: A): A = A.min(lhs, rhs)
def max(rhs: A): A = A.max(lhs, rhs)
def compare(rhs: A): Int = macro Ops.binop[A, Int]
def min(rhs: A): A = macro Ops.binop[A, A]
def max(rhs: A): A = macro Ops.binop[A, A]
}
23 changes: 12 additions & 11 deletions core/src/main/scala/cats/syntax/partialOrder.scala
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
package cats
package syntax

import cats.macros.Ops
import scala.language.experimental.macros

trait PartialOrderSyntax {
// TODO: use simulacrum instances eventually
implicit def partialOrderSyntax[A: PartialOrder](a: A) =
new PartialOrderOps[A](a)
implicit def partialOrderSyntax[A: PartialOrder](a: A) = new PartialOrderOps[A](a)
}

class PartialOrderOps[A](lhs: A)(implicit A: PartialOrder[A]) {
def >(rhs: A): Boolean = A.gt(lhs, rhs)
def >=(rhs: A): Boolean = A.gteqv(lhs, rhs)
def <(rhs: A): Boolean = A.lt(lhs, rhs)
def <=(rhs: A): Boolean = A.lteqv(lhs, rhs)
def >(rhs: A): Boolean = macro Ops.binop[A, Boolean]
def >=(rhs: A): Boolean = macro Ops.binop[A, Boolean]
def <(rhs: A): Boolean = macro Ops.binop[A, Boolean]
def <=(rhs: A): Boolean = macro Ops.binop[A, Boolean]

def partialCompare(rhs: A): Double = A.partialCompare(lhs, rhs)
def tryCompare(rhs: A): Option[Int] = A.tryCompare(lhs, rhs)
def pmin(rhs: A): Option[A] = A.pmin(lhs, rhs)
def pmax(rhs: A): Option[A] = A.pmax(lhs, rhs)
def partialCompare(rhs: A): Double = macro Ops.binop[A, Double]
def tryCompare(rhs: A): Option[Int] = macro Ops.binop[A, Option[Int]]
def pmin(rhs: A): Option[A] = macro Ops.binop[A, Option[A]]
def pmax(rhs: A): Option[A] = macro Ops.binop[A, Option[A]]
}
18 changes: 18 additions & 0 deletions macros/src/main/scala/cats/macros/Ops.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package cats.macros

object Ops extends machinist.Ops {

def uesc(c: Char): String = "$u%04X".format(c.toInt)

val operatorNames: Map[String, String] =
Map(
("$eq$eq$eq", "eqv"),
("$eq$bang$eq", "neqv"),
("$greater", "gt"),
("$greater$eq", "gteqv"),
("$less", "lt"),
("$less$eq", "lteqv"),
("$bar$plus$bar", "combine"),
("$bar$minus$bar", "remove")
)
}

1 comment on commit ce14308

@stew
Copy link
Contributor

@stew stew commented on ce14308 Feb 5, 2015

Choose a reason for hiding this comment

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

Nice. 👍

Please sign in to comment.