aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPiotr Krzemiński <pio.krzeminski@gmail.com>2017-06-21 13:17:43 +0100
committerPiotr Krzemiński <pio.krzeminski@gmail.com>2017-06-21 13:20:49 +0100
commit2b4ef02a92e431b16d9b314def1da204fd1f9ab1 (patch)
treea948830efec3e573130e4dc037b3a3f46fce1efc
parentd1a7565a92d81a451c50d7739365d7106f63f1b7 (diff)
downloadmagnolia-2b4ef02a92e431b16d9b314def1da204fd1f9ab1.tar.gz
magnolia-2b4ef02a92e431b16d9b314def1da204fd1f9ab1.tar.bz2
magnolia-2b4ef02a92e431b16d9b314def1da204fd1f9ab1.zip
initial support for 2-arg contravariant derivations; works for simple Eq typeclass, still wip
-rw-r--r--core/src/main/scala/magnolia.scala91
-rw-r--r--examples/src/main/scala/example.scala32
-rw-r--r--tests/shared/src/main/scala/magnolia/main.scala16
3 files changed, 103 insertions, 36 deletions
diff --git a/core/src/main/scala/magnolia.scala b/core/src/main/scala/magnolia.scala
index d2978b5..4ce2ec3 100644
--- a/core/src/main/scala/magnolia.scala
+++ b/core/src/main/scala/magnolia.scala
@@ -44,28 +44,23 @@ class Macros(val c: whitebox.Context) {
typeConstructor: Type,
assignedName: TermName,
derivationImplicit: Either[Tree, Tree]): Tree = {
-
+
+ val searchType = appliedType(typeConstructor, genericType)
findType(genericType).map { methodName =>
val methodAsString = methodName.encodedName.toString
- val searchType = appliedType(typeConstructor, genericType)
q"_root_.magnolia.Lazy.apply[$searchType]($methodAsString)"
}.orElse {
- val searchType = appliedType(typeConstructor, genericType)
- findType(genericType).map { _ =>
- directInferImplicit(genericType, typeConstructor, derivationImplicit)
- }.getOrElse {
- scala.util.Try {
- val genericTypeName: String = genericType.typeSymbol.name.encodedName.toString.toLowerCase
- val assignedName: TermName = TermName(c.freshName(s"${genericTypeName}Typeclass"))
- recurse(ChainedImplicit(genericType.toString), genericType, assignedName) {
- val inferredImplicit = c.inferImplicitValue(searchType, false, false)
- q"""{
- def $assignedName: $searchType = $inferredImplicit
- $assignedName
- }"""
- }.get
- }.toOption.orElse(directInferImplicit(genericType, typeConstructor, derivationImplicit))
- }
+ scala.util.Try {
+ val genericTypeName: String = genericType.typeSymbol.name.encodedName.toString.toLowerCase
+ val assignedName: TermName = TermName(c.freshName(s"${genericTypeName}Typeclass"))
+ recurse(ChainedImplicit(genericType.toString), genericType, assignedName) {
+ val inferredImplicit = c.inferImplicitValue(searchType, false, false)
+ q"""{
+ def $assignedName: $searchType = $inferredImplicit
+ $assignedName
+ }"""
+ }.get
+ }.toOption.orElse(directInferImplicit(genericType, typeConstructor, derivationImplicit))
}.getOrElse {
val currentStack: Stack = recursionStack(c.enclosingPosition)
@@ -115,9 +110,16 @@ class Macros(val c: whitebox.Context) {
val dereferencedValue = q"$impl.dereference(sourceParameter, ${param.name.toString})"
q"$impl.call($derivedImplicit, $dereferencedValue)"
case Right(impl) =>
+// val paramName = TermName(param.name.toString)
+// val dereferencedValue = q"sourceParameter.$paramName"
+// q"$impl.call($derivedImplicit, $dereferencedValue)"
+
val paramName = TermName(param.name.toString)
- val dereferencedValue = q"sourceParameter.$paramName"
- q"$impl.call($derivedImplicit, $dereferencedValue)"
+ val dereferencedValue1 = q"sourceParameter1.$paramName"
+ val dereferencedValue2 = q"sourceParameter2.$paramName"
+ q"$impl.call($derivedImplicit, $dereferencedValue1, $dereferencedValue2)"
+
+
}
}
@@ -154,16 +156,28 @@ class Macros(val c: whitebox.Context) {
val reduction = components.reduce { (left, right) => q"$impl.combine($left, $right)" }
q"$impl.call($reduction, sourceParameter)"
case Right(impl) =>
+
val parts = subtypes.tail.zip(components.tail)
-
+
+// val base = q"""
+// $impl.call(${components.head}, sourceParameter.asInstanceOf[${subtypes.head}])
+// """
+//
+// parts.foldLeft(base) { case (aggregated, (componentType, derivedImplicit)) =>
+// q"""
+// if(sourceParameter.isInstanceOf[$componentType])
+// $impl.call($derivedImplicit, sourceParameter.asInstanceOf[$componentType])
+// else $aggregated"""
+// }
+
val base = q"""
- $impl.call(${components.head}, sourceParameter.asInstanceOf[${subtypes.head}])
+ $impl.call(${components.head}, sourceParameter1.asInstanceOf[${subtypes.head}], sourceParameter2.asInstanceOf[${subtypes.head}])
"""
-
+
parts.foldLeft(base) { case (aggregated, (componentType, derivedImplicit)) =>
q"""
- if(sourceParameter.isInstanceOf[$componentType])
- $impl.call($derivedImplicit, sourceParameter.asInstanceOf[$componentType])
+ if(sourceParameter1.isInstanceOf[$componentType] && sourceParameter2.isInstanceOf[$componentType])
+ $impl.call($derivedImplicit, sourceParameter1.asInstanceOf[$componentType], sourceParameter2.asInstanceOf[$componentType])
else $aggregated"""
}
}
@@ -172,10 +186,16 @@ class Macros(val c: whitebox.Context) {
construct.map { const =>
val impl = derivationImplicit.merge
+// q"""{
+// def $assignedName: $resultType = $impl.construct { sourceParameter => $const }
+// $assignedName
+// }"""
+
q"""{
- def $assignedName: $resultType = $impl.construct { sourceParameter => $const }
+ def $assignedName: $resultType = $impl.construct { case (sourceParameter1, sourceParameter2) => $const }
$assignedName
}"""
+
}
}
@@ -188,18 +208,21 @@ class Macros(val c: whitebox.Context) {
val coDerivationTypeclass = weakTypeOf[CovariantDerivation[_]].typeConstructor
val contraDerivationTypeclass = weakTypeOf[ContravariantDerivation[_]].typeConstructor
-
+ val contraDerivation2Typeclass = weakTypeOf[ContravariantDerivation2[_]].typeConstructor
+
val coDerivationType = appliedType(coDerivationTypeclass, List(typeConstructor))
- val contraDerivationType = appliedType(contraDerivationTypeclass, List(typeConstructor))
+// val contraDerivationType = appliedType(contraDerivationTypeclass, List(typeConstructor))
+ val contraDerivation2Type = appliedType(contraDerivation2Typeclass, List(typeConstructor))
+
val derivationImplicit = try {
Left(c.untypecheck(c.inferImplicitValue(coDerivationType, false, false)))
} catch {
case e: Exception =>
- try Right(c.untypecheck(c.inferImplicitValue(contraDerivationType, false, false))) catch {
+ try Right(c.untypecheck(c.inferImplicitValue(contraDerivation2Type, false, false))) catch {
case e: Exception =>
c.info(c.enclosingPosition, s"could not find an implicit instance of "+
s"CovariantDerivation[$typeConstructor] or "+
- s"ContravariantDerivation[$typeConstructor]", true)
+ s"ContravariantDerivation2[$typeConstructor]", true)
throw e
}
@@ -226,7 +249,6 @@ class Macros(val c: whitebox.Context) {
Some(q"_root_.magnolia.Lazy[$searchType]($methodAsString)")
}
} else {
- val typeConstructor: Type = weakTypeOf[Typeclass].typeConstructor
directInferImplicit(genericType, typeConstructor, derivationImplicit)
}
@@ -301,5 +323,12 @@ trait ContravariantDerivation[Typeclass[_]] {
def call[T](typeclass: Typeclass[T], value: T): Return
def construct[T](body: T => Return): Typeclass[T]
def join(elements: ListMap[String, Return]): Return
+}
+trait ContravariantDerivation2[Typeclass[_]] {
+ type Return
+ def call[T](typeclass: Typeclass[T], value1: T, value2: T): Return
+ def construct[T](body: (T, T) => Return): Typeclass[T]
+ def join(elements: ListMap[String, Return]): Return
}
+
diff --git a/examples/src/main/scala/example.scala b/examples/src/main/scala/example.scala
index e649b88..c649fba 100644
--- a/examples/src/main/scala/example.scala
+++ b/examples/src/main/scala/example.scala
@@ -14,13 +14,23 @@ object `package` {
implicit val showBool: Show[Boolean] = _.toString
implicit def showList[T: Show]: Show[List[T]] = xs => xs.map { x => s"list:${implicitly[Show[T]].show(x)}" }.mkString(";")
implicit def showSet[T: Show]: Show[Set[T]] = s => "set"
+
+ implicit class Equable[T: Eq](t: T) {
+ def isEqualTo(other: T): Boolean = implicitly[Eq[T]].isEqual(t, other)
+ }
+ implicit val eqString: Eq[String] = _ == _
+ implicit val eqBool: Eq[Boolean] = _ == _
+ implicit def eqList[T: Eq]: Eq[List[T]] =
+ (l1, l2) => l1.size == l2.size && (l1 zip l2).forall { case (e1, e2) => e1 isEqualTo e2 }
+ implicit def eqSet[T: Eq]: Eq[Set[T]] =
+ (s1, s2) => s1.size == s2.size && (s1 zip s2).forall { case (e1, e2) => e1 isEqualTo e2 }
}
sealed trait EmptyType
sealed trait Tree
case class Branch(left: Tree, right: Tree) extends Tree
-case class Leaf(value: Int, no: EmptyType) extends Tree
+case class Leaf(value: Int, no: String) extends Tree
sealed trait Entity
case class Person(name: String, address: Address) extends Entity
@@ -42,3 +52,23 @@ object Show extends Show_1 {
trait Show_1 {
implicit def generic[T]: Show[T] = macro Macros.magnolia[T, Show[_]]
}
+
+trait Eq[T] { def isEqual(a: T, b: T): Boolean }
+
+object Eq extends Eq_1 {
+
+ implicit val eqInt: Eq[Int] = _ == _
+
+ implicit val derivation = new ContravariantDerivation2[Eq] {
+ type Return = Boolean
+ def call[T](eq: Eq[T], value1: T, value2: T): Boolean =
+ if(value1.getClass == value2.getClass) eq.isEqual(value1, value2) else false
+ def construct[T](body: (T, T) => Boolean): Eq[T] = body(_, _)
+ def join(elements: ListMap[String, Boolean]): Boolean = elements.forall(_._2)
+ }
+}
+
+trait Eq_1 {
+ implicit def generic[T]: Eq[T] = macro Macros.magnolia[T, Eq[_]]
+}
+
diff --git a/tests/shared/src/main/scala/magnolia/main.scala b/tests/shared/src/main/scala/magnolia/main.scala
index 014ec33..c56d617 100644
--- a/tests/shared/src/main/scala/magnolia/main.scala
+++ b/tests/shared/src/main/scala/magnolia/main.scala
@@ -4,10 +4,18 @@ import examples._
object Main {
def main(args: Array[String]): Unit = {
- println(Branch(Branch(Leaf(1, null), Leaf(2, null)), Leaf(3, null)).show)
- println(List[Entity](Person("John Smith",
- Address(List("1 High Street", "London", "SW1A 1AA"),
- Country("UK", "GBR", false)))).show)
+
+ val tree1: Tree = Branch(Branch(Leaf(1, "abc"), Leaf(2, "def")), Leaf(3, "ghi"))
+ val tree2: Tree = Branch(Leaf(1, "abc"), Leaf(2, "def"))
+
+// println(tree1.show)
+ println(tree1 isEqualTo tree1)
+ println(tree1 isEqualTo tree2)
+
+// println(List[Entity](Person("John Smith",
+// Address(List("1 High Street", "London", "SW1A 1AA"),
+// Country("UK", "GBR", false)))).show)
+
}
}