簡體   English   中英

宏拼接期間出現“錯誤:缺少參數類型”

[英]“error: missing parameter type” during macro splicing

編輯:我發現了我的錯誤-我的遞歸案例的准引用中有一個錯誤,導致它返回格式錯誤的序列


我正在嘗試創建一個將案例類T轉換為updateMap: Map[String, (play.api.libs.json.JsValue) => Try[(T) => T]]的宏updateMap: Map[String, (play.api.libs.json.JsValue) => Try[(T) => T]]如何使用scala宏以創建函數對象(以創建Map [String,(T)=> T]) ),其中映射的鍵是case類的字段名稱-這個想法是給定JsObject("key" -> JsValue)我們可以使用keyupdateMap檢索適當的更新方法,然后使用JsValue應用更新。 我在非遞歸情況下進行此工作,即給定一個沒有任何其他案例類作為字段的案例類。 但是,我想擴展此宏,以便它可以為包含其他案例類的案例類生成updateMap

case class Inner(innerStr: String)
case class Outer(outerStr: String, inner: Inner)

updateMap[Outer] = {
  // updateMap[Inner]
  val innerMap = Map("innerStr" -> (str: String) => 
    Try { (i: Inner) => i.copy(innerStr = str) } )

  // updateMap[Outer]
  Map("outerStr" -> (str: String) => 
    Try { (o: Outer) => o.copy(outerStr = str) },
  "inner.innerStr" -> (str: String) => 
    Try { (o: Outer) => innerMap.get("innerStr").get(str).flatMap(lens => o.copy(inner = lens(o.inner))) })}

換句話說,給定updateMap[Outer] ,我將能夠直接更新對象的outerStr字段,否則,我將能夠更新對象的inner.innerStr字段,無論哪種方式都返回Try[Outer]

該代碼對於非遞歸情況( copyMapRec[Inner]() )正常工作,但是遞歸情況( copyMapRec[Outer]() )給我一個“錯誤:缺少參數類型”錯誤-我假設我要么需要在某個地方提供一個隱式參數,否則我對拼接有一個基本的誤解。

下面的代碼使用(String) => Try[(T) => T]而不是(JsValue) => Try[(T) => T]因此我不需要將play框架導入到我的REPL。 我使用隱式轉換將JsValue (或String )轉換為適當的類型(在val x: $fieldType = str發生val x: $fieldType = str行,在基本情況下為准引號;如果沒有適當的隱式轉換,則得到編譯器錯誤)。

import scala.language.experimental.macros

def copyMapImplRec[T: c.WeakTypeTag](c: scala.reflect.macros.Context)(blacklist: c.Expr[String]*): c.Expr[Map[String, (String) => scala.util.Try[(T) => T]]] = {
  import c.universe._

  // Fields that should be omitted from the map
  val blacklistList: Seq[String] = blacklist.map(e => c.eval(c.Expr[String](c.resetAllAttrs(e.tree))))

  def rec(tpe: Type): c.Expr[Map[String, (String) => scala.util.Try[(T) => T]]] = {
    val typeName = tpe.typeSymbol.name.decoded

    // All fields in the case class's primary constructor, minus the blacklisted fields
    val fields = tpe.declarations.collectFirst {
      case m: MethodSymbol if m.isPrimaryConstructor => m
    }.get.paramss.head.filterNot(field => blacklistList.contains(typeName + "." + field.name.decoded))

    // Split the fields into case classes and non case classes
    val recursive = fields.filter(f => f.typeSignature.typeSymbol.isClass && f.typeSignature.typeSymbol.asClass.isCaseClass)
    val nonRecursive = fields.filterNot(f => f.typeSignature.typeSymbol.isClass && f.typeSignature.typeSymbol.asClass.isCaseClass)

    val recursiveMethods = recursive.map {
      field => {
        val fieldName = field.name
        val fieldNameDecoded = fieldName.decoded
        // Get the c.Expr[Map] for this case class
        val map = rec(field.typeSignature)
        // Construct an "inner.innerStr -> " seq of tuples from the "innerStr -> " seq of tuples
      q"""{
          val innerMap = $map
          innerMap.toSeq.map(tuple => ($fieldNameDecoded + "." + tuple._1) -> {
          (str: String) => {
            val innerUpdate = tuple._2(str)
            innerUpdate.map(innerUpdate => (outer: $tpe) => outer.copy($fieldName = innerUpdate(outer.$fieldName)))
          }
      })}"""
      }
    }

    val nonRecursiveMethods = nonRecursive.map {
      field => {
        val fieldName = field.name
        val fieldNameDecoded = fieldName.decoded
        val fieldType = field.typeSignature
        val fieldTypeName = fieldType.toString
        q"""{
          $fieldNameDecoded -> {
            (str: String) => scala.util.Try {
              val x: $fieldType = str
              (t: $tpe) => t.copy($fieldName = x)
            }.recoverWith {
              case e: Exception => scala.util.Failure(new IllegalArgumentException("Failed to parse " + str + " as " + $typeName + "." + $fieldNameDecoded + ": " + $fieldTypeName))
            }
         }}"""
      }
    }

    // Splice in all of the sequences of tuples, flatten the sequence, and construct a map
    c.Expr[Map[String, (String) => scala.util.Try[(T) => T]]] {
      q"""{ Map((List(..$recursiveMethods).flatten ++ List(..$nonRecursiveMethods)):_*) }"""
    }
  }

  rec(weakTypeOf[T])

}

def copyMapRec[T](blacklist: String*) = macro copyMapImplRec[T]

我解決了這個問題-最初在我的recursiveMethods准引用具有innerMap.toSeq(...)而不是innerMap.toSeq.map(...) -我忽略了先在REPL中測試代碼

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM