diff options
-rw-r--r-- | core/src/main/scala/magnolia.scala | 30 | ||||
-rw-r--r-- | examples/src/main/scala/typeclasses.scala | 7 | ||||
-rw-r--r-- | tests/src/main/scala/tests.scala | 22 |
3 files changed, 32 insertions, 27 deletions
diff --git a/core/src/main/scala/magnolia.scala b/core/src/main/scala/magnolia.scala index c2b280f..cabe88e 100644 --- a/core/src/main/scala/magnolia.scala +++ b/core/src/main/scala/magnolia.scala @@ -37,8 +37,6 @@ object Magnolia { val typeConstructor: c.Type = c.prefix.tree.tpe.member(TypeName("Typeclass")).asType.toType.typeConstructor - println(typeConstructor) - def findType(key: Type): Option[TermName] = recursionStack(c.enclosingPosition).frames.find(_.genericType == key).map(_.termName(c)) @@ -113,6 +111,7 @@ object Magnolia { val resultType = appliedType(typeConstructor, genericType) + println(s"Deriving $genericType") // FIXME: Handle AnyVals if(isCaseObject) { @@ -138,34 +137,35 @@ object Magnolia { } val className = genericType.toString - val implicits: List[(c.universe.MethodSymbol, c.Tree)] = caseClassParameters.map { param => + val implicits: List[(c.universe.MethodSymbol, c.Tree, c.Type)] = caseClassParameters.map { param => val paramName = param.name.encodedName.toString + val paramType = param.returnType.substituteTypes(genericType.etaExpand.typeParams, genericType.typeArgs) val derivedImplicit = recurse(ProductType(paramName, genericType.toString), genericType, assignedName) { - implicitTree(Some(paramName), param.returnType, typeConstructor, assignedName) + implicitTree(Some(paramName), paramType, typeConstructor, assignedName) }.getOrElse { c.abort(c.enclosingPosition, s"failed to get implicit for type $genericType") } - (param, derivedImplicit) + (param, derivedImplicit, paramType) }.to[List] Some { - val callables = implicits.map { case (param, imp) => + val callables = implicits.map { case (param, imp, paramType) => val label = param.name.toString q"""new _root_.magnolia.Param[$typeConstructor, ${genericType}] { - type S = ${param.returnType} - def typeclass: ${appliedType(typeConstructor, param.returnType)} = $imp + type S = ${paramType} + def typeclass: ${appliedType(typeConstructor, paramType)} = $imp def label: _root_.java.lang.String = $label - def dereference(param: ${genericType}): ${param.returnType} = param.${TermName(label)} + def dereference(param: ${genericType}): ${paramType} = param.${TermName(label)} }""" } val constructor = q"""new $genericType(..${callables.zip(implicits).map { case (call, imp) => - q"fn($call).asInstanceOf[${imp._1.returnType}]" + q"fn($call).asInstanceOf[${imp._3}]" } })""" val impl = q""" @@ -184,7 +184,12 @@ object Magnolia { """ } } else if(isSealedTrait) { - val subtypes = classType.get.knownDirectSubclasses.to[List] + val genericSubtypes = classType.get.knownDirectSubclasses.to[List] + val subtypes = genericSubtypes.map { sub => + val mapping = sub.asType.typeSignature.baseType(genericType.typeSymbol).typeArgs.zip(genericType.typeArgs).toMap + val newTypeParams = sub.asType.toType.typeArgs.map(mapping(_)) + appliedType(sub.asType.toType.typeConstructor, newTypeParams) + } if(subtypes.isEmpty) { c.info(c.enclosingPosition, @@ -195,7 +200,7 @@ object Magnolia { Some { - val subclasses = subtypes.map(_.asType.toType).map { searchType => + val subclasses = subtypes.map { searchType => recurse(CoproductType(genericType.toString), genericType, assignedName) { (searchType, implicitTree(None, searchType, typeConstructor, assignedName)) }.getOrElse { @@ -265,6 +270,7 @@ object Magnolia { result.map { tree => val out = if(currentStack.frames.isEmpty) c.untypecheck(removeDeferred.transform(tree)) else tree + println(out) out }.getOrElse { c.abort(c.enclosingPosition, s"magnolia: could not infer typeclass for type $genericType") diff --git a/examples/src/main/scala/typeclasses.scala b/examples/src/main/scala/typeclasses.scala index 589a075..440c5bc 100644 --- a/examples/src/main/scala/typeclasses.scala +++ b/examples/src/main/scala/typeclasses.scala @@ -81,10 +81,9 @@ object Decoder { trait Decoder[T] { def decode(str: String): T } -sealed trait Tree -case class Leaf(value: String) extends Tree -case class Branch(left: Tree, right: Tree) extends Tree -case object Bud extends Tree +sealed trait Tree[+T] +case class Leaf[+L](value: L) extends Tree[L] +case class Branch[+B](left: Tree[B], right: Tree[B]) extends Tree[B] sealed trait Entity diff --git a/tests/src/main/scala/tests.scala b/tests/src/main/scala/tests.scala index ee3faf0..aea6cc3 100644 --- a/tests/src/main/scala/tests.scala +++ b/tests/src/main/scala/tests.scala @@ -23,11 +23,11 @@ object Tests extends TestApp { Show.generic[Person].show(Person("John Smith", 34)) }.assert(_ == "Person(name=John Smith,age=34)") - Show.generic[Tree] + //Show.generic[Tree[String]] test("serialize a Branch") { import magnolia.examples._ - implicitly[Show[String, Branch]].show(Branch(Leaf("LHS"), Leaf("RHS"))) + implicitly[Show[String, Branch[String]]].show(Branch(Leaf("LHS"), Leaf("RHS"))) }.assert(_ == "Branch(left=Leaf(value=LHS),right=Leaf(value=RHS))") test("test equality false") { @@ -42,7 +42,7 @@ object Tests extends TestApp { test("test branch equality true") { import examples._ - Eq.generic[Tree].equal(Branch(Leaf("one"), Leaf("two")), Branch(Leaf("one"), Leaf("two"))) + Eq.generic[Tree[String]].equal(Branch(Leaf("one"), Leaf("two")), Branch(Leaf("one"), Leaf("two"))) }.assert(_ == true) test("construct a default value") { @@ -52,23 +52,23 @@ object Tests extends TestApp { test("construction of Show instance for Leaf") { scalac""" import magnolia.examples._ - implicitly[Show[String, Leaf]] + implicitly[Show[String, Leaf[String]]] """ - }.assert(_ == (Returns(fqt"magnolia.examples.Show[String,magnolia.examples.Leaf]"): Compilation)) + }.assert(_ == (Returns(fqt"magnolia.examples.Show[String,magnolia.examples.Leaf[java.lang.String]]"): Compilation)) test("construction of Show instance for Tree") { scalac""" import magnolia.examples._ - implicitly[Show[String, Tree]] + implicitly[Show[String, Tree[String]]] """ - }.assert(_ == (Returns(fqt"magnolia.examples.Show[String,magnolia.examples.Tree]"): Compilation)) + }.assert(_ == (Returns(fqt"magnolia.examples.Show[String,magnolia.examples.Tree[String]]"): Compilation)) test("serialize a Leaf") { - implicitly[Show[String, Leaf]].show(Leaf("testing")) + implicitly[Show[String, Leaf[String]]].show(Leaf("testing")) }.assert(_ == "Leaf(value=testing)") test("serialize a Branch as a Tree") { - implicitly[Show[String, Tree]].show(Branch(Leaf("LHS"), Leaf("RHS"))) + implicitly[Show[String, Tree[String]]].show(Branch(Leaf("LHS"), Leaf("RHS"))) }.assert(_ == "Branch(left=Leaf(value=LHS),right=Leaf(value=RHS))") test("show error stack") { @@ -84,8 +84,8 @@ object Tests extends TestApp { |"""): Compilation)) //test("construct a decoder") { - //Decoder.generic[Tree].decode("string") - //}.assert(_ == (Leaf("something"): Tree)) + //Decoder.generic[Tree[String]].decode("string") + //}.assert(_ == (Leaf("something"): Tree[String])) } } |