From 8283109f8cd0bb91071c4b8be01dba91e020ac6e Mon Sep 17 00:00:00 2001 From: The Gitter Badger Date: Mon, 12 Jun 2017 16:48:44 +0000 Subject: Add Gitter badge --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ebc850e..b4f1876 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ # Magnolia +[![Join the chat at https://gitter.im/propensive/magnolia](https://badges.gitter.im/propensive/magnolia.svg)](https://gitter.im/propensive/magnolia?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + Magnolia is a generic macro for automatic derivation of typeclasses for datatypes composed from case classes and sealed traits. It supports recursively-defined datatypes out-of-the-box, and incurs no significant -- cgit v1.2.3 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 --- core/src/main/scala/magnolia.scala | 91 ++++++++++++++++--------- examples/src/main/scala/example.scala | 32 ++++++++- tests/shared/src/main/scala/magnolia/main.scala | 16 +++-- 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) + } } -- cgit v1.2.3 From 64f817404963d6222fae91ccd259e497c5023e3e Mon Sep 17 00:00:00 2001 From: Piotr Krzemiński Date: Wed, 21 Jun 2017 13:38:20 +0100 Subject: defined DerivationImplicit --- core/src/main/scala/magnolia.scala | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/core/src/main/scala/magnolia.scala b/core/src/main/scala/magnolia.scala index 4ce2ec3..f96f23e 100644 --- a/core/src/main/scala/magnolia.scala +++ b/core/src/main/scala/magnolia.scala @@ -12,6 +12,13 @@ class Macros(val c: whitebox.Context) { import c.universe._ import CompileTimeState._ + + sealed trait DerivationImplicit { def tree: Tree } + case class CovariantDerivationImplicit(tree: Tree) extends DerivationImplicit + case class ContravariantDerivationImplicit(tree: Tree) extends DerivationImplicit + case class ContravariantDerivation2Implicit(tree: Tree) extends DerivationImplicit + + private def findType(key: Type): Option[TermName] = recursionStack(c.enclosingPosition).frames.find(_.genericType == key).map(_.termName(c)) @@ -43,7 +50,7 @@ class Macros(val c: whitebox.Context) { genericType: Type, typeConstructor: Type, assignedName: TermName, - derivationImplicit: Either[Tree, Tree]): Tree = { + derivationImplicit: DerivationImplicit): Tree = { val searchType = appliedType(typeConstructor, genericType) findType(genericType).map { methodName => @@ -75,7 +82,7 @@ class Macros(val c: whitebox.Context) { private def directInferImplicit(genericType: Type, typeConstructor: Type, - derivationImplicit: Either[Tree, Tree]): Option[Tree] = { + derivationImplicit: DerivationImplicit): Option[Tree] = { val genericTypeName: String = genericType.typeSymbol.name.encodedName.toString.toLowerCase val assignedName: TermName = TermName(c.freshName(s"${genericTypeName}Typeclass")) @@ -106,10 +113,10 @@ class Macros(val c: whitebox.Context) { } derivationImplicit match { - case Left(impl) => + case CovariantDerivationImplicit(impl) => val dereferencedValue = q"$impl.dereference(sourceParameter, ${param.name.toString})" q"$impl.call($derivedImplicit, $dereferencedValue)" - case Right(impl) => + case ContravariantDerivation2Implicit(impl) => // val paramName = TermName(param.name.toString) // val dereferencedValue = q"sourceParameter.$paramName" // q"$impl.call($derivedImplicit, $dereferencedValue)" @@ -124,9 +131,9 @@ class Macros(val c: whitebox.Context) { } derivationImplicit match { - case Left(_) => + case CovariantDerivationImplicit(_) => Some(q"new $genericType(..$implicits)") - case Right(impl) => + case ContravariantDerivation2Implicit(impl) => val namedImplicits = caseClassParameters.zip(implicits).map { case (param, tree) => q"(${param.name.encodedName.toString}, $tree)" } @@ -152,10 +159,10 @@ class Macros(val c: whitebox.Context) { } derivationImplicit match { - case Left(impl) => + case CovariantDerivationImplicit(impl) => val reduction = components.reduce { (left, right) => q"$impl.combine($left, $right)" } q"$impl.call($reduction, sourceParameter)" - case Right(impl) => + case ContravariantDerivation2Implicit(impl) => val parts = subtypes.tail.zip(components.tail) @@ -185,7 +192,7 @@ class Macros(val c: whitebox.Context) { } else None construct.map { const => - val impl = derivationImplicit.merge + val impl = derivationImplicit.tree // q"""{ // def $assignedName: $resultType = $impl.construct { sourceParameter => $const } // $assignedName @@ -215,10 +222,10 @@ class Macros(val c: whitebox.Context) { val contraDerivation2Type = appliedType(contraDerivation2Typeclass, List(typeConstructor)) val derivationImplicit = try { - Left(c.untypecheck(c.inferImplicitValue(coDerivationType, false, false))) + CovariantDerivationImplicit(c.untypecheck(c.inferImplicitValue(coDerivationType, false, false))) } catch { case e: Exception => - try Right(c.untypecheck(c.inferImplicitValue(contraDerivation2Type, false, false))) catch { + try ContravariantDerivation2Implicit(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 "+ -- cgit v1.2.3 From 6bf80848cd0e729fc37998314c06dd26a4e230bf Mon Sep 17 00:00:00 2001 From: Piotr Krzemiński Date: Wed, 21 Jun 2017 13:57:12 +0100 Subject: bring back support for 1-arg type classes; now both Show and Eq are supported --- core/src/main/scala/magnolia.scala | 120 ++++++++++++------------ tests/shared/src/main/scala/magnolia/main.scala | 8 +- 2 files changed, 64 insertions(+), 64 deletions(-) diff --git a/core/src/main/scala/magnolia.scala b/core/src/main/scala/magnolia.scala index f96f23e..61a00d6 100644 --- a/core/src/main/scala/magnolia.scala +++ b/core/src/main/scala/magnolia.scala @@ -2,7 +2,6 @@ package magnolia import scala.reflect._, macros._ import macrocompat.bundle -import scala.util.Try import scala.collection.immutable.ListMap import language.existentials import language.higherKinds @@ -15,8 +14,9 @@ class Macros(val c: whitebox.Context) { sealed trait DerivationImplicit { def tree: Tree } case class CovariantDerivationImplicit(tree: Tree) extends DerivationImplicit - case class ContravariantDerivationImplicit(tree: Tree) extends DerivationImplicit - case class ContravariantDerivation2Implicit(tree: Tree) extends DerivationImplicit + sealed trait ContravariantDerivationImplicit extends DerivationImplicit + case class ContravariantDerivation1Implicit(tree: Tree) extends ContravariantDerivationImplicit + case class ContravariantDerivation2Implicit(tree: Tree) extends ContravariantDerivationImplicit private def findType(key: Type): Option[TermName] = @@ -29,14 +29,14 @@ class Macros(val c: whitebox.Context) { recursionStack.get(c.enclosingPosition).map(_.push(path, key, value)).getOrElse( Stack(List(Frame(path, key, value)), Nil)) ) - + try Some(fn) catch { case e: Exception => None } finally { val currentStack = recursionStack(c.enclosingPosition) recursionStack = recursionStack.updated(c.enclosingPosition, currentStack.pop()) } } - + private val removeLazy: Transformer = new Transformer { override def transform(tree: Tree): Tree = tree match { case q"_root_.magnolia.Lazy.apply[$returnType](${Literal(Constant(method: String))})" => @@ -45,7 +45,7 @@ class Macros(val c: whitebox.Context) { super.transform(tree) } } - + private def getImplicit(paramName: Option[String], genericType: Type, typeConstructor: Type, @@ -70,16 +70,16 @@ class Macros(val c: whitebox.Context) { }.toOption.orElse(directInferImplicit(genericType, typeConstructor, derivationImplicit)) }.getOrElse { val currentStack: Stack = recursionStack(c.enclosingPosition) - + val error = ImplicitNotFound(genericType.toString, recursionStack(c.enclosingPosition).frames.map(_.path)) - - val updatedStack = currentStack.copy(errors = error :: currentStack.errors) + + val updatedStack = currentStack.copy(errors = error :: currentStack.errors) recursionStack = recursionStack.updated(c.enclosingPosition, updatedStack) c.abort(c.enclosingPosition, s"Could not find type class for type $genericType") } } - + private def directInferImplicit(genericType: Type, typeConstructor: Type, derivationImplicit: DerivationImplicit): Option[Tree] = { @@ -91,7 +91,7 @@ class Macros(val c: whitebox.Context) { val isCaseClass = classType.map(_.isCaseClass).getOrElse(false) val isSealedTrait = classType.map(_.isSealed).getOrElse(false) val isValueClass = genericType <:< typeOf[AnyVal] - + val resultType = appliedType(typeConstructor, genericType) val construct = if(isCaseClass) { @@ -101,44 +101,42 @@ class Macros(val c: whitebox.Context) { val implicits = caseClassParameters.map { param => val paramName = param.name.encodedName.toString - + val derivedImplicit = recurse(ProductType(paramName, genericType.toString), genericType, assignedName) { - + getImplicit(Some(paramName), param.returnType, typeConstructor, assignedName, derivationImplicit) - + }.getOrElse { c.abort(c.enclosingPosition, s"failed to get implicit for type $genericType") } - + derivationImplicit match { case CovariantDerivationImplicit(impl) => val dereferencedValue = q"$impl.dereference(sourceParameter, ${param.name.toString})" q"$impl.call($derivedImplicit, $dereferencedValue)" + case ContravariantDerivation1Implicit(impl) => + val paramName = TermName(param.name.toString) + val dereferencedValue = q"sourceParameter.$paramName" + q"$impl.call($derivedImplicit, $dereferencedValue)" case ContravariantDerivation2Implicit(impl) => -// val paramName = TermName(param.name.toString) -// val dereferencedValue = q"sourceParameter.$paramName" -// q"$impl.call($derivedImplicit, $dereferencedValue)" - val paramName = TermName(param.name.toString) val dereferencedValue1 = q"sourceParameter1.$paramName" val dereferencedValue2 = q"sourceParameter2.$paramName" q"$impl.call($derivedImplicit, $dereferencedValue1, $dereferencedValue2)" - - } } derivationImplicit match { case CovariantDerivationImplicit(_) => Some(q"new $genericType(..$implicits)") - case ContravariantDerivation2Implicit(impl) => + case contra: ContravariantDerivationImplicit => val namedImplicits = caseClassParameters.zip(implicits).map { case (param, tree) => q"(${param.name.encodedName.toString}, $tree)" } - Some(q"$impl.join(_root_.scala.collection.immutable.ListMap(..$namedImplicits))") + Some(q"${contra.tree}.join(_root_.scala.collection.immutable.ListMap(..$namedImplicits))") } } else if(isSealedTrait) { @@ -162,25 +160,24 @@ class Macros(val c: whitebox.Context) { case CovariantDerivationImplicit(impl) => val reduction = components.reduce { (left, right) => q"$impl.combine($left, $right)" } q"$impl.call($reduction, sourceParameter)" - case ContravariantDerivation2Implicit(impl) => + 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""" + } -// 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""" $impl.call(${components.head}, sourceParameter1.asInstanceOf[${subtypes.head}], sourceParameter2.asInstanceOf[${subtypes.head}]) """ - parts.foldLeft(base) { case (aggregated, (componentType, derivedImplicit)) => q""" if(sourceParameter1.isInstanceOf[$componentType] && sourceParameter2.isInstanceOf[$componentType]) @@ -192,17 +189,19 @@ class Macros(val c: whitebox.Context) { } else None construct.map { const => - val impl = derivationImplicit.tree -// q"""{ -// def $assignedName: $resultType = $impl.construct { sourceParameter => $const } -// $assignedName -// }""" - - q"""{ - def $assignedName: $resultType = $impl.construct { case (sourceParameter1, sourceParameter2) => $const } - $assignedName - }""" + derivationImplicit match { + case ContravariantDerivation1Implicit(impl) => + q"""{ + def $assignedName: $resultType = $impl.construct { sourceParameter => $const } + $assignedName + }""" + case ContravariantDerivation2Implicit(impl) => + q"""{ + def $assignedName: $resultType = $impl.construct { case (sourceParameter1, sourceParameter2) => $const } + $assignedName + }""" + } } } @@ -218,23 +217,24 @@ class Macros(val c: whitebox.Context) { 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 { - CovariantDerivationImplicit(c.untypecheck(c.inferImplicitValue(coDerivationType, false, false))) - } catch { - case e: Exception => - try ContravariantDerivation2Implicit(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"ContravariantDerivation2[$typeConstructor]", true) - - throw e - } + + val derivationImplicitTry = scala.util.Try[DerivationImplicit](CovariantDerivationImplicit(c.untypecheck(c.inferImplicitValue(coDerivationType, false, false)))) + .orElse(scala.util.Try(ContravariantDerivation1Implicit(c.untypecheck(c.inferImplicitValue(contraDerivationType, false, false))))) + .orElse(scala.util.Try(ContravariantDerivation2Implicit(c.untypecheck(c.inferImplicitValue(contraDerivation2Type, false, false))))) + + val derivationImplicit: DerivationImplicit = if(derivationImplicitTry.isFailure) { + c.info(c.enclosingPosition, s"could not find an implicit instance of "+ + s"CovariantDerivation[$typeConstructor] or "+ + s"ContravariantDerivation[$typeConstructor] or "+ + s"ContravariantDerivation2[$typeConstructor]", true) + throw derivationImplicitTry.failed.get + } else { + derivationImplicitTry.get } - + if(directlyReentrant) throw DirectlyReentrantException() currentStack.errors.foreach { error => diff --git a/tests/shared/src/main/scala/magnolia/main.scala b/tests/shared/src/main/scala/magnolia/main.scala index c56d617..9449f8d 100644 --- a/tests/shared/src/main/scala/magnolia/main.scala +++ b/tests/shared/src/main/scala/magnolia/main.scala @@ -8,13 +8,13 @@ object Main { 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.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) + println(List[Entity](Person("John Smith", + Address(List("1 High Street", "London", "SW1A 1AA"), + Country("UK", "GBR", false)))).show) } } -- cgit v1.2.3 From ed7010d896a5eb0ff3c182b67ce7cf6c0fad7b44 Mon Sep 17 00:00:00 2001 From: Piotr Krzemiński Date: Thu, 22 Jun 2017 09:18:00 +0100 Subject: refactor --- core/src/main/scala/magnolia.scala | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/core/src/main/scala/magnolia.scala b/core/src/main/scala/magnolia.scala index 61a00d6..27172d3 100644 --- a/core/src/main/scala/magnolia.scala +++ b/core/src/main/scala/magnolia.scala @@ -191,6 +191,8 @@ class Macros(val c: whitebox.Context) { construct.map { const => derivationImplicit match { + case CovariantDerivationImplicit(_) => + ??? case ContravariantDerivation1Implicit(impl) => q"""{ def $assignedName: $resultType = $impl.construct { sourceParameter => $const } @@ -206,6 +208,7 @@ class Macros(val c: whitebox.Context) { } def magnolia[T: WeakTypeTag, Typeclass: WeakTypeTag]: Tree = { + import scala.util.{Try, Success, Failure} val genericType: Type = weakTypeOf[T] val currentStack: Stack = recursionStack.get(c.enclosingPosition).getOrElse(Stack(List(), List())) @@ -220,20 +223,22 @@ class Macros(val c: whitebox.Context) { val contraDerivationType = appliedType(contraDerivationTypeclass, List(typeConstructor)) val contraDerivation2Type = appliedType(contraDerivation2Typeclass, List(typeConstructor)) - - val derivationImplicitTry = scala.util.Try[DerivationImplicit](CovariantDerivationImplicit(c.untypecheck(c.inferImplicitValue(coDerivationType, false, false)))) - .orElse(scala.util.Try(ContravariantDerivation1Implicit(c.untypecheck(c.inferImplicitValue(contraDerivationType, false, false))))) - .orElse(scala.util.Try(ContravariantDerivation2Implicit(c.untypecheck(c.inferImplicitValue(contraDerivation2Type, false, false))))) - - val derivationImplicit: DerivationImplicit = if(derivationImplicitTry.isFailure) { - c.info(c.enclosingPosition, s"could not find an implicit instance of "+ - s"CovariantDerivation[$typeConstructor] or "+ - s"ContravariantDerivation[$typeConstructor] or "+ - s"ContravariantDerivation2[$typeConstructor]", true) - throw derivationImplicitTry.failed.get - } else { - derivationImplicitTry.get - } + def findDerivationImplicit[T <: DerivationImplicit](tpe: c.Type, cons: Tree => T): Try[DerivationImplicit] = + Try(cons(c.untypecheck(c.inferImplicitValue(tpe, false, false)))) + + val derivationImplicit = + findDerivationImplicit(coDerivationType, CovariantDerivationImplicit) + .orElse(findDerivationImplicit(contraDerivationType, ContravariantDerivation1Implicit)) + .orElse(findDerivationImplicit(contraDerivation2Type, ContravariantDerivation2Implicit)) match { + case Failure(e) => + c.info(c.enclosingPosition, s"could not find an implicit instance of "+ + s"CovariantDerivation[$typeConstructor] or "+ + s"ContravariantDerivation[$typeConstructor] or "+ + s"ContravariantDerivation2[$typeConstructor]", true) + throw e + case Success(di) => + di + } if(directlyReentrant) throw DirectlyReentrantException() -- 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(-) 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