aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorJon Pretty <jon.pretty@propensive.com>2017-11-24 14:26:17 +0000
committerJon Pretty <jon.pretty@propensive.com>2017-11-24 14:26:17 +0000
commitd20a3824ce7719deb328ffed002288bedc398f77 (patch)
tree3c5cb04da66710498759439f8e8c068a0e9502ac /core
parent192173d811f870e6e9c705c80118e27dc63a898b (diff)
downloadmagnolia-d20a3824ce7719deb328ffed002288bedc398f77.tar.gz
magnolia-d20a3824ce7719deb328ffed002288bedc398f77.tar.bz2
magnolia-d20a3824ce7719deb328ffed002288bedc398f77.zip
Use virtualized `param` if it is available
Diffstat (limited to 'core')
-rw-r--r--core/shared/src/main/scala/interface.scala8
-rw-r--r--core/shared/src/main/scala/magnolia.scala83
2 files changed, 49 insertions, 42 deletions
diff --git a/core/shared/src/main/scala/interface.scala b/core/shared/src/main/scala/interface.scala
index 54f8ce3..25dd1fd 100644
--- a/core/shared/src/main/scala/interface.scala
+++ b/core/shared/src/main/scala/interface.scala
@@ -91,11 +91,11 @@ trait Param[Typeclass[_], Type] {
* @param parametersArray an array of [[Param]] values for this case class
* @tparam Typeclass type constructor for the typeclass being derived
* @tparam Type generic type of this parameter */
-abstract class CaseClass[Typeclass[_], Type] private[magnolia] (
+abstract class CaseClass[Typeclass[_], Type, ParamType] private[magnolia] (
val typeName: String,
val isObject: Boolean,
val isValueClass: Boolean,
- parametersArray: Array[Param[Typeclass, Type]]
+ parametersArray: Array[ParamType]
) {
/** constructs a new instance of the case class type
@@ -111,13 +111,13 @@ abstract class CaseClass[Typeclass[_], Type] private[magnolia] (
* @param makeParam lambda for converting a generic [[Param]] into the value to be used for
* this parameter in the construction of a new instance of the case class
* @return a new instance of the case class */
- def construct[Return](makeParam: Param[Typeclass, Type] => Return): Type
+ def construct[Return](makeParam: ParamType => Return): Type
/** a sequence of [[Param]] objects representing all of the parameters in the case class
*
* For efficiency, this sequence is implemented by an `Array`, but upcast to a
* [[scala.collection.Seq]] to hide the mutable collection API. */
- def parameters: Seq[Param[Typeclass, Type]] = parametersArray
+ def parameters: Seq[ParamType] = parametersArray
}
/** represents a sealed trait and the context required to construct a new typeclass instance
diff --git a/core/shared/src/main/scala/magnolia.scala b/core/shared/src/main/scala/magnolia.scala
index 6400002..0e5728c 100644
--- a/core/shared/src/main/scala/magnolia.scala
+++ b/core/shared/src/main/scala/magnolia.scala
@@ -81,41 +81,44 @@ object Magnolia {
else q"${tpe.typeSymbol.name.toTermName}"
}
- val typeDefs = prefixType.baseClasses.flatMap { cls =>
- cls.asType.toType.decls.filter(_.isType).find(_.name.toString == "Typeclass").map { tpe =>
- tpe.asType.toType.asSeenFrom(prefixType, cls)
+
+ def getTypeMember(name: String) = {
+ val typeDefs = prefixType.baseClasses.flatMap { cls =>
+ cls.asType.toType.decls.filter(_.isType).find(_.name.toString == name).map { tpe =>
+ tpe.asType.toType.asSeenFrom(prefixType, cls)
+ }
}
- }
- val typeConstructorOpt =
- typeDefs.headOption.map(_.typeConstructor)
+ val typeConstructorOpt = typeDefs.headOption.map(_.typeConstructor)
- val typeConstructor = typeConstructorOpt.getOrElse {
- c.abort(c.enclosingPosition,
- "magnolia: the derivation object does not define the Typeclass type constructor")
+ typeConstructorOpt.getOrElse {
+ c.abort(c.enclosingPosition,
+ s"magnolia: the derivation object does not define the $name type constructor")
+ }
}
- def checkMethod(termName: String, category: String, expected: String) = {
+ val typeConstructor = getTypeMember("Typeclass")
+ val pTypeConstructor = getTypeMember("ParamType")
+
+ def getMethod[T](termName: String): Option[MethodSymbol] = {
val term = TermName(termName)
val combineClass = c.prefix.tree.tpe.baseClasses
.find { cls =>
cls.asType.toType.decl(term) != NoSymbol
}
- .getOrElse {
- c.abort(
- c.enclosingPosition,
- s"magnolia: the method `$termName` must be defined on the derivation object to derive typeclasses for $category"
- )
- }
- val firstParamBlock = combineClass.asType.toType.decl(term).asTerm.asMethod.paramLists.head
- if (firstParamBlock.length != 1)
- c.abort(c.enclosingPosition,
- s"magnolia: the method `combine` should take a single parameter of type $expected")
+ combineClass.map { c =>
+ c.asType.toType.decl(term).asTerm.asMethod
+ }
}
// FIXME: Only run these methods if they're used, particularly `dispatch`
- checkMethod("combine", "case classes", "CaseClass[Typeclass, _]")
- checkMethod("dispatch", "sealed traits", "SealedTrait[Typeclass, _]")
+ getMethod("combine").getOrElse(c.abort(c.enclosingPosition,
+ s"magnolia: the method `dispatch` should take a single parameter of type CaseClass[Typeclass, _]")
+ )
+
+ getMethod("dispatch").getOrElse(c.abort(c.enclosingPosition,
+ s"magnolia: the method `combine` should take a single parameter of type SealedTrait[Typeclass, _]")
+ )
def findType(key: Type): Option[TermName] =
recursionStack(c.enclosingPosition).frames.find(_.genericType == key).map(_.termName(c))
@@ -226,8 +229,12 @@ object Magnolia {
val obj = companionRef(genericType)
val className = genericType.typeSymbol.name.decodedName.toString
+ val paramType: Tree = getMethod("param").map { sym =>
+ tq"${c.prefix}.ParamType[$genericType, _]"
+ }.getOrElse(tq"$magnoliaPkg.Param[$typeConstructor, $genericType]")
+
val impl = q"""
- ${c.prefix}.combine($magnoliaPkg.Magnolia.caseClass[$typeConstructor, $genericType](
+ ${c.prefix}.combine($magnoliaPkg.Magnolia.caseClass[$typeConstructor, $genericType, $paramType](
$className, true, false, new $scalaPkg.Array(0), _ => $obj)
)
"""
@@ -294,10 +301,10 @@ object Magnolia {
val assignments = caseParams.zip(defaults).zipWithIndex.map {
case ((CaseParam(param, typeclass, paramType, ref), defaultVal), idx) =>
- q"""$paramsVal($idx) = $magnoliaPkg.Magnolia.param[$typeConstructor, $genericType,
- $paramType](
- ${param.name.decodedName.toString}, $ref, $defaultVal, _.${param.name}
- )"""
+ val paramMethod: Tree = getMethod("param").map { sym =>
+ q"${c.prefix}.param[$genericType, $paramType]"
+ }.getOrElse(q"$magnoliaPkg.Magnolia.param[$typeConstructor, $genericType, $paramType]")
+ q"""$paramsVal($idx) = $paramMethod(${param.name.decodedName.toString}, $ref, $defaultVal, _.${param.name})"""
}
Some(
@@ -305,20 +312,20 @@ object Magnolia {
genericType,
q"""{
..$preAssignments
- val $paramsVal: $scalaPkg.Array[$magnoliaPkg.Param[$typeConstructor, $genericType]] =
+ val $paramsVal: $scalaPkg.Array[${c.prefix}.ParamType[$genericType, _]] =
new $scalaPkg.Array(${assignments.length})
..$assignments
- ${c.prefix}.combine($magnoliaPkg.Magnolia.caseClass[$typeConstructor, $genericType](
+ ${c.prefix}.combine($magnoliaPkg.Magnolia.caseClass[$typeConstructor, $genericType, ${c.prefix}.ParamType[$genericType, _]](
$className,
false,
$isValueClass,
$paramsVal,
- ($fnVal: $magnoliaPkg.Param[$typeConstructor, $genericType] => Any) =>
+ ($fnVal: ${c.prefix}.ParamType[$genericType, _] => Any) =>
new $genericType(..${caseParams.zipWithIndex.map {
- case (typeclass, idx) =>
- q"$fnVal($paramsVal($idx)).asInstanceOf[${typeclass.paramType}]"
- }})
+ case (typeclass, idx) =>
+ q"$fnVal($paramsVal($idx)).asInstanceOf[${typeclass.paramType}]"
+ }})
))
}"""
)
@@ -464,13 +471,13 @@ object Magnolia {
*
* This method is intended to be called only from code generated by the Magnolia macro, and
* should not be called directly from users' code. */
- def caseClass[Tc[_], T](name: String,
+ def caseClass[Tc[_], T, ParamType](name: String,
obj: Boolean,
valClass: Boolean,
- params: Array[Param[Tc, T]],
- constructor: (Param[Tc, T] => Any) => T) =
- new CaseClass[Tc, T](name, obj, valClass, params) {
- def construct[R](param: Param[Tc, T] => R): T = constructor(param)
+ params: Array[ParamType],
+ constructor: (ParamType => Any) => T) =
+ new CaseClass[Tc, T, ParamType](name, obj, valClass, params) {
+ def construct[R](param: ParamType => R): T = constructor(param)
}
}