From 2b4ef02a92e431b16d9b314def1da204fd1f9ab1 Mon Sep 17 00:00:00 2001 From: Piotr KrzemiƄski Date: Wed, 21 Jun 2017 13:17:43 +0100 Subject: initial support for 2-arg contravariant derivations; works for simple Eq typeclass, still wip --- examples/src/main/scala/example.scala | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) (limited to 'examples') 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[_]] +} + -- cgit v1.2.3 From 6e702a5a5fb9f9d508e0a9ddbbbfc2cb64267971 Mon Sep 17 00:00:00 2001 From: Jon Pretty Date: Thu, 22 Jun 2017 18:47:32 +0200 Subject: Use case clauses and switch statement instead of is/asInstanceOf --- core/src/main/scala/magnolia.scala | 22 ++++++++++------------ examples/src/main/scala/example.scala | 4 +--- tests/shared/src/main/scala/magnolia/main.scala | 6 ++++-- 3 files changed, 15 insertions(+), 17 deletions(-) (limited to 'examples') diff --git a/core/src/main/scala/magnolia.scala b/core/src/main/scala/magnolia.scala index 27172d3..66d59d0 100644 --- a/core/src/main/scala/magnolia.scala +++ b/core/src/main/scala/magnolia.scala @@ -161,18 +161,6 @@ class Macros(val c: whitebox.Context) { val reduction = components.reduce { (left, right) => q"$impl.combine($left, $right)" } q"$impl.call($reduction, sourceParameter)" - case ContravariantDerivation1Implicit(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""" - } - case ContravariantDerivation2Implicit(impl) => val parts = subtypes.tail.zip(components.tail) val base = q""" @@ -184,6 +172,16 @@ class Macros(val c: whitebox.Context) { $impl.call($derivedImplicit, sourceParameter1.asInstanceOf[$componentType], sourceParameter2.asInstanceOf[$componentType]) else $aggregated""" } + case ContravariantDerivation1Implicit(impl) => + val parts = subtypes.zip(components) + + val caseClauses = parts.map { case (subtype, component) => + cq"(value: $subtype) => $impl.call($component, value)" + } + + q"""(sourceParameter: @_root_.scala.annotation.switch) match { + case ..$caseClauses + }""" } } } else None diff --git a/examples/src/main/scala/example.scala b/examples/src/main/scala/example.scala index c649fba..543fa3d 100644 --- a/examples/src/main/scala/example.scala +++ b/examples/src/main/scala/example.scala @@ -26,11 +26,9 @@ object `package` { (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: String) extends Tree +case class Leaf(value: Int) extends Tree sealed trait Entity case class Person(name: String, address: Address) extends Entity diff --git a/tests/shared/src/main/scala/magnolia/main.scala b/tests/shared/src/main/scala/magnolia/main.scala index 9449f8d..3ac3fb6 100644 --- a/tests/shared/src/main/scala/magnolia/main.scala +++ b/tests/shared/src/main/scala/magnolia/main.scala @@ -5,13 +5,15 @@ import examples._ object Main { def main(args: Array[String]): Unit = { - val tree1: Tree = Branch(Branch(Leaf(1, "abc"), Leaf(2, "def")), Leaf(3, "ghi")) - val tree2: Tree = Branch(Leaf(1, "abc"), Leaf(2, "def")) + val tree1: Tree = Branch(Branch(Leaf(1), Leaf(2)), Leaf(3)) + val tree2: Tree = Branch(Leaf(1), Leaf(2)) println(tree1.show) println(tree1 isEqualTo tree1) println(tree1 isEqualTo tree2) + println(Branch(Branch(Leaf(1), Leaf(2)), Leaf(3)).show) + println(List[Entity](Person("John Smith", Address(List("1 High Street", "London", "SW1A 1AA"), Country("UK", "GBR", false)))).show) -- cgit v1.2.3