aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJon Pretty <jon.pretty@propensive.com>2017-11-01 11:36:14 +0100
committerJon Pretty <jon.pretty@propensive.com>2017-11-01 11:36:14 +0100
commit25fb02b13b721004269f9bd9a4e5d22c2bb219c4 (patch)
treeadeb4ff353d959dfa14e17d726b0d2f939ab2b3e
parent88f36a7a552246e210266a7ae82a2e480718512e (diff)
downloadmagnolia-25fb02b13b721004269f9bd9a4e5d22c2bb219c4.tar.gz
magnolia-25fb02b13b721004269f9bd9a4e5d22c2bb219c4.tar.bz2
magnolia-25fb02b13b721004269f9bd9a4e5d22c2bb219c4.zip
Support for GADTs (including recursive ones)
Some tests still failing, but macros are working
-rw-r--r--core/src/main/scala/magnolia.scala30
-rw-r--r--examples/src/main/scala/typeclasses.scala7
-rw-r--r--tests/src/main/scala/tests.scala22
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]))
}
}