Skip to content
Open
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
10 changes: 4 additions & 6 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
/RUNNING_PID
/logs/
/project/*-shim.sbt
/project/project/
/project/target/
/target/
project/*-shim.sbt
project/project/
project/target/
target/
/.idea/
/choices/target/
/predicates/target/
77 changes: 77 additions & 0 deletions macros/src/main/scala/com/wheaties/predicate/Macros.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.wheaties.predicate

import annotation.{compileTimeOnly, StaticAnnotation}
import scala.reflect.macros.whitebox
import scala.reflect.macros._
import scala.language.experimental.macros

object Macros {

@compileTimeOnly("GeneratePredicate must be called during compilation")
class GeneratePredicate extends StaticAnnotation {
def macroTransform(annottees: Any*): Any = macro GeneratePredicate.impl
}

object GeneratePredicate {
def impl(c: whitebox.Context)(annottees: c.Expr[Any]*): c.Expr[Any] = {
import c.universe._

println("bazinga")

def modifiedClass(classDecl: ClassDef) = {
println(s"bazinga ${classDecl.name}")
c.Expr(
q"""
trait ${classDecl.name}[T1] extends Function1[T1, Boolean]{
self =>

def or[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{
def apply(arg1: TT1) = self(arg1) || that(arg1)
}
def and[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{
def apply(arg1: TT1) = self(arg1) && that(arg1)
}
def xor[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{
def apply(arg1: TT1) = if(self(arg1)) !that(arg1) else that(arg1)
}
def nor[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{
def apply(arg1: TT1) = !(self(arg1) || that(arg1))
}
def nand[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{
def apply(arg1: TT1) = !(self(arg1) && that(arg1))
}
def nxor[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{
def apply(arg1: TT1) = if(self(arg1)) that(arg1) else !that(arg1)
}
def not = new Predicate1[T1]{
def apply(arg1: T1) = !self(arg1)
}
override def toString() = "<predicate1>"

}

object ${classDecl.name.toTermName} {
object Always extends ${classDecl.name}[Any]{
def apply(arg1: Any) = true
}

val always = Always

object Never extends ${classDecl.name}[Any]{
def apply(arg1: Any) = false
}

val never = Never
}
""")
}

annottees.map(_.tree) match {
case (classDecl: ClassDef) :: Nil => modifiedClass(classDecl)
case _ => c.abort(c.enclosingPosition, "Invalid annottee")
}
}

}
}

41 changes: 3 additions & 38 deletions predicates/src/main/scala/com/wheaties/predicate/Predicates1.scala
Original file line number Diff line number Diff line change
@@ -1,41 +1,6 @@
package com.wheaties.predicate

import com.wheaties.logical._
import com.wheaties.predicate.Macros.GeneratePredicate

trait Predicate1[T1] extends Function1[T1, Boolean]{
self =>

def or[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{
def apply(arg1: TT1) = self(arg1) || that(arg1)
}
def and[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{
def apply(arg1: TT1) = self(arg1) && that(arg1)
}
def xor[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{
def apply(arg1: TT1) = if(self(arg1)) !that(arg1) else that(arg1)
}
def nor[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{
def apply(arg1: TT1) = !(self(arg1) || that(arg1))
}
def nand[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{
def apply(arg1: TT1) = !(self(arg1) && that(arg1))
}
def nxor[TT1 <: T1](that: Function1[TT1, Boolean]) = new Predicate1[TT1]{
def apply(arg1: TT1) = if(self(arg1)) that(arg1) else !that(arg1)
}
override def toString() = "<predicate1>"

}
object Predicate1{
implicit def not[T1] = new Negation[Predicate1[T1]]{
def not(pred: Predicate1[T1]) = new Predicate1[T1]{
def apply(arg1: T1) = !pred(arg1)
}
}
}
object Always1 extends Predicate1[Any]{
def apply(arg1: Any) = true
}
object Never1 extends Predicate1[Any]{
def apply(arg1: Any) = false
}
@GeneratePredicate
trait Predicate1
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,24 @@ class PredicateSpec extends WordSpecLike
val filtered = sampleList.filter(Modulo(2, 0) or (Modulo(3, 0) and LessThen(6)))
filtered should equal(List(2, 3, 4, 6))
}

"have implicit `not` method" in new SpecExamples {
// val filtered = sampleList.filter(Predicate1.not.not(Modulo(2, 0)))
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Commented out line worked with old API. Predicate.not.not looked weird so I simply changed it to a method. Maybe I was missing something - how it was intended to be used?

val filtered = sampleList.filter(Modulo(2, 0).not)
filtered should equal(List(3, 5))
}

"have accompanying Always" in new SpecExamples {
// val filtered = sampleList.filter(Modulo(2, 0) or Always1)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Commented out line worked with old API. Such API is impossible to achieve using just macro annotation because it may return just annotated type and its companion object (no other top level objects). So instead I create inner Object which is then available via field Predicate1.always

val filtered = sampleList.filter(Modulo(2, 0) or Predicate1.always)
filtered should equal(sampleList)
}

"have accompanying Never" in new SpecExamples {
// val filtered = sampleList.filter(Modulo(2, 0) and Never1)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Same thing as with Always

val filtered = sampleList.filter(Modulo(2, 0) and Predicate1.never)
filtered should equal(List.empty)
}
}

trait SpecExamples {
Expand Down
35 changes: 28 additions & 7 deletions project/build.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,43 @@ import sbt.Keys._
import xerial.sbt.Sonatype._

object Predicates extends Build{
val predicates = Project(

lazy val macros: Project = Project(
"macros",
file("macros"),
settings = commonSettings ++ Seq(
libraryDependencies += "org.scala-lang" % "scala-reflect" % "2.11.8"
)
)

lazy val predicates = Project(
id = "predicates",
base = file("predicates"),
settings = Project.defaultSettings ++ baseSettings ++ Seq(
libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.1" % "test"
settings = baseSettings ++ Seq(
libraryDependencies += "org.scalatest" %% "scalatest" % "2.2.1" % "test",
resolvers += Resolver.sonatypeRepo("snapshots"),
resolvers += Resolver.sonatypeRepo("releases"),
addCompilerPlugin("org.scalamacros" % "paradise" % "2.1.0" cross CrossVersion.full)
)
)
).dependsOn(macros)

val commonSettings = {
val paradiseVersion = "2.1.0"
Seq(
scalaVersion := "2.11.8",
resolvers += Resolver.sonatypeRepo("snapshots"),
resolvers += Resolver.sonatypeRepo("releases"),
addCompilerPlugin("org.scalamacros" % "paradise" % paradiseVersion cross CrossVersion.full)
)
}

val baseSettings = Seq(
version := "0.1",
scalaVersion := "2.11.6",
organization := "com.github.wheaties",
scalacOptions := Seq("-deprecation",
"-encoding", "UTF-8",
"-feature",
"-language:higherKinds",
"-language:higherKinds",
"-language:existentials",
"-unchecked",
"-Xfatal-warnings",
Expand All @@ -36,7 +57,7 @@ object Predicates extends Build{
pomIncludeRepository := { x => false },
publishMavenStyle := true,
publishArtifact in Test := false
)
) ++ commonSettings

val predicatesPom =
<url>http://github.com/wheaties/Predicates</url>
Expand Down