Scala:q插值器内的模式

时间:2018-01-25 12:39:01

标签: scala macros scala-quasiquotes

我正在查看一段Scala宏,它提供了一个类的隐式实现。此类将字段值的映射转换为案例类。可以找到宏herethis就是它背后的解释。

目前,该实现忽略了输入映射中提供的冗余字段。我想添加一个类似于fromMap的方法,如果输入映射有冗余条目会引发异常,但我不确定我是否理解它。

我的理解是toMapParamsfromMapParams是接受输入并在其上应用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 )

}

我该怎么做?

1 个答案:

答案 0 :(得分:1)

不幸的是,你没有提供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)
      }
    }
  """
  }
}