比例:q 插值器内的图案
Scala: pattern inside a q interpolator
我正在查看一段 Scala 宏,它提供了 class 的隐式实现。此 class 将字段值映射转换为大小写 class。宏可以找到 here and this 是它背后的解释。
目前,该实现忽略了输入映射中提供的冗余字段。我想添加一个类似于 fromMap
的方法,如果输入映射有冗余条目,它会抛出异常,但我不确定我是否理解得足够好。
我的理解是 toMapParams
和 fromMapParams
是接受输入并对它们应用 Map
或 Companion 对象的 Apply 方法的表达式。
因此,应修改 fromMapParams
以将冗余值输出为以下形式的异常字符串列表:
case class CaseClassRedundantFieldException[T]
(redundantFields: List[String], cause: Throwable = None.orNull)
(implicit c: ClassTag[T])
extends Exception(s"Conversion between map of data-fields to ${c.runtimeClass.asInstanceOf[T]} failed"
+ "as redundant fields were provided: $redundantFields",
cause)
我想我只需要有类似的东西:
def fromMap(map: Map[String, Any]): $tpe = {
val redundant vals: fields diff $map
if(vals.size > 0){ CaseClassRedundantFieldException(vals) } //(these two lines don't have the right syntax.)
$companion(..$fromMapParams )
}
我该怎么做?
很遗憾,您没有提供 Minimal, Complete, and Verifiable example 您现在拥有的内容,所以我不得不回到您开始时的内容。我认为这个修改后的宏所做的事情与您想要的非常相似:
def materializeMappableImpl[T: c.WeakTypeTag](c: Context): c.Expr[Mappable[T]] = {
import c.universe._
val tpe = weakTypeOf[T]
val className = tpe.typeSymbol.name.toString
val companion = tpe.typeSymbol.companion
val fields = tpe.decls.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor ⇒ m
}.get.paramLists.head
val (toMapParams, fromMapParams, fromMapParamsList) = fields.map { field ⇒
val name = field.name.toTermName
val decoded = name.decodedName.toString
val returnType = tpe.decl(name).typeSignature
(q"$decoded → t.$name", q"map($decoded).asInstanceOf[$returnType]", decoded)
}.unzip3
c.Expr[Mappable[T]] {
q"""
new Mappable[$tpe] {
private val fieldNames = scala.collection.immutable.Set[String](..$fromMapParamsList)
def toMap(t: $tpe): Map[String, Any] = Map(..$toMapParams)
def fromMap(map: Map[String, Any]): $tpe = {
val redundant = map.keys.filter(k => !fieldNames.contains(k))
if(!redundant.isEmpty) throw new IllegalArgumentException("Conversion between map of data-fields to " + $className + " failed because there are redundant fields: " + redundant.mkString("'","', ","'"))
$companion(..$fromMapParams)
}
}
"""
}
}
我正在查看一段 Scala 宏,它提供了 class 的隐式实现。此 class 将字段值映射转换为大小写 class。宏可以找到 here and this 是它背后的解释。
目前,该实现忽略了输入映射中提供的冗余字段。我想添加一个类似于 fromMap
的方法,如果输入映射有冗余条目,它会抛出异常,但我不确定我是否理解得足够好。
我的理解是 toMapParams
和 fromMapParams
是接受输入并对它们应用 Map
或 Companion 对象的 Apply 方法的表达式。
因此,应修改 fromMapParams
以将冗余值输出为以下形式的异常字符串列表:
case class CaseClassRedundantFieldException[T]
(redundantFields: List[String], cause: Throwable = None.orNull)
(implicit c: ClassTag[T])
extends Exception(s"Conversion between map of data-fields to ${c.runtimeClass.asInstanceOf[T]} failed"
+ "as redundant fields were provided: $redundantFields",
cause)
我想我只需要有类似的东西:
def fromMap(map: Map[String, Any]): $tpe = {
val redundant vals: fields diff $map
if(vals.size > 0){ CaseClassRedundantFieldException(vals) } //(these two lines don't have the right syntax.)
$companion(..$fromMapParams )
}
我该怎么做?
很遗憾,您没有提供 Minimal, Complete, and Verifiable example 您现在拥有的内容,所以我不得不回到您开始时的内容。我认为这个修改后的宏所做的事情与您想要的非常相似:
def materializeMappableImpl[T: c.WeakTypeTag](c: Context): c.Expr[Mappable[T]] = {
import c.universe._
val tpe = weakTypeOf[T]
val className = tpe.typeSymbol.name.toString
val companion = tpe.typeSymbol.companion
val fields = tpe.decls.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor ⇒ m
}.get.paramLists.head
val (toMapParams, fromMapParams, fromMapParamsList) = fields.map { field ⇒
val name = field.name.toTermName
val decoded = name.decodedName.toString
val returnType = tpe.decl(name).typeSignature
(q"$decoded → t.$name", q"map($decoded).asInstanceOf[$returnType]", decoded)
}.unzip3
c.Expr[Mappable[T]] {
q"""
new Mappable[$tpe] {
private val fieldNames = scala.collection.immutable.Set[String](..$fromMapParamsList)
def toMap(t: $tpe): Map[String, Any] = Map(..$toMapParams)
def fromMap(map: Map[String, Any]): $tpe = {
val redundant = map.keys.filter(k => !fieldNames.contains(k))
if(!redundant.isEmpty) throw new IllegalArgumentException("Conversion between map of data-fields to " + $className + " failed because there are redundant fields: " + redundant.mkString("'","', ","'"))
$companion(..$fromMapParams)
}
}
"""
}
}