2014-01-18 90 views
1

編輯:我已經解決了這個問題 - 我被錯誤地呼喚.map(f => f.typeSignature.asInstanceOf[TypeRef].args.head)recursiveOpt,這意味着field.name是給我在我的copy錯誤的字段名方法。我已經刪除了map,現在一切正常。崩潰了編譯器「MatchError:AnyRef」當我打電話給我的斯卡拉宏觀


我正在寫一個宏,它將爲案例類創建更新方法的映射,例如,

case class Inner(innerStr: String) 
case class Outer(outerStr: String, inner: Inner, innerOpt: Option[Inner]) 

應該產生於外的更新地圖這就是說,

val innerMap = Map("innerStr" -> {(json: JsValue) => Try{(inner: Inner) => inner.copy(innerStr = json)}}) 

val outerMap = Map("outerStr" -> {(json: JsValue) => Try{(outer: Outer) => outer.copy(outerStr = json)}}, 
    "inner.innerStr" -> {(json: JsValue) => Try{(outer: Outer) => innerMap.get("innerStr").get(json).flatMap(update => outer.copy(inner = update(outer.inner)))}, 
    "innerOpt.innerStr" -> {(json: JsValue) => Try{(outer: Outer) => innerMap.get("innerStr").get(json).flatMap(update => outer.copy(inner = outer.inner.map(inner => update(inner))))}) 

這將被稱爲像

val oldOuter = Outer("str", Inner("str"), Some(Inner("str"))) 
val updatedOuter = outerMap.get("inner.innerStr").get(JsString("newStr")).get(oldOuter) 

的想法是,給定一個JSON KV對,我可以使用該鍵從映射中檢索適當的更新方法,然後使用該值應用更新,使用隱式轉換將json值轉換爲適當的類型。

我的宏正在處理一個平坦的案例類,例如, Inner(innerStr: String),對於嵌套案例類,例如Outer(outerStr: String, inner: Inner)。但是,嵌套選項案例類Outer(outerStr: String, innerOpt: Option[Inner])的情況會導致編譯器崩潰。我不確定我是否做了一件災難性的錯誤,或者如果編譯器中有錯誤,或者是第三種選擇。這是使用Scala的2.11.0-M7 REPL

下面要做的就是我的代碼 - 我構建Map接受String輸入,而不是JsValue輸入,這樣我就不需要導入播放框架變成我的REPL 。 blacklist將不應包含在更新映射中的字段過濾掉(例如,我們正在應用此類的一個案例類具有不應通過通過REST路由發送的json更新的「crypted_pa​​ssword」和「salt」字段) 。 baseMethods構造扁平大小寫的鍵 - >方法元組,recursiveMethods構造嵌套大小寫的鍵 - 方法元組,recursiveOptMethods構造嵌套選項大小寫的鍵 - 值元組;在宏的底部,這些都被合併成一個平坦的序列,並放置在Map中。

我測試了recursiveOptMethods quasiquotes中的代碼,以確保我構造了一個正確類型的元組序列,並且沒有發現錯誤(此代碼與recursiveMethods quasiquotes非常相似,正確),並且我已經測試了構建baserecursiverecursiveOpt符號序列的代碼,並且它們似乎在做他們的工作。

任何幫助,爲什麼我崩潰的編譯器將不勝感激。


import scala.language.experimental.macros 

def copyTestImpl[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._ 

    val blacklistList: Seq[String] = blacklist.map(e => c.eval(c.Expr[String](c.resetAllAttrs(e.tree)))) 

    def isCaseClass(tpe: Type): Boolean = tpe.typeSymbol.isClass && tpe.typeSymbol.asClass.isCaseClass 

    def isCaseClassOpt(tpe: Type): Boolean = tpe.typeSymbol.name.decoded == "Option" && isCaseClass(tpe.asInstanceOf[TypeRef].args.head) 

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

    val fields = tpe.declarations.collectFirst { 
     case m: MethodSymbol if m.isPrimaryConstructor => m 
    }.get.paramss.head.filterNot(field => blacklistList.contains(typeName + "." + field.name.decoded)) 

    val recursive = fields.filter(f => isCaseClass(f.typeSignature)) 
    val recursiveOpt = fields.filter(f => isCaseClassOpt(f.typeSignature)) 
    val base = fields.filterNot(f => isCaseClass(f.typeSignature) || isCaseClassOpt(f.typeSignature)) 

    val recursiveMethods = recursive.map { 
     field => { 
     val fieldName = field.name 
     val fieldNameDecoded = fieldName.decoded 
     val map = rec(field.typeSignature) 
     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 recursiveOptMethods = recursiveOpt.map { 
     field => { 
     val fieldName = field.name 
     val fieldNameDecoded = fieldName.decoded 
     val map = rec(field.typeSignature.asInstanceOf[TypeRef].args.head) 
     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 = (outer.$fieldName).map(inner => innerUpdate(inner)))) 
      } 
     })}""" 
     }} 

    val baseMethods = base.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)) 
      } 
     }}""" 
     }} 

    c.Expr[Map[String, (String) => scala.util.Try[(T) => T]]] { 
     q"""{ Map((List(..$recursiveMethods).flatten ++ List(..$recursiveOptMethods).flatten ++ List(..$baseMethods)):_*) }""" 
    } 
    } 

    rec(weakTypeOf[T]) 
} 

def copyTest[T](blacklist: String*) = macro copyTestImpl[T] 

而且從2.11我的錯誤的頂部和底部。0-M7 REPL調用copyTest[Outer]()時(其中OuterOption[Inner]場)

scala> copyTest[Outer]() 

scala.MatchError: AnyRef 
    with Product 
    with Serializable { 
    val innerStr: String 
    private[this] val innerStr: String 
    def <init>(innerStr: String): Inner 
    def copy(innerStr: String): Inner 
    def copy$default$1: String @scala.annotation.unchecked.uncheckedVariance 
    override def productPrefix: String 
    def productArity: Int 
    def productElement(x$1: Int): Any 
    override def productIterator: Iterator[Any] 
    def canEqual(x$1: Any): Boolean 
    override def hashCode(): Int 
    override def toString(): String 
    override def equals(x$1: Any): Boolean 
} (of class scala.reflect.internal.Types$ClassInfoType) 
    at scala.reflect.internal.Variances$class.inType$1(Variances.scala:181) 
    at scala.reflect.internal.Variances$$anonfun$inArgs$1$1.apply(Variances.scala:176) 
    at scala.reflect.internal.Variances$$anonfun$inArgs$1$1.apply(Variances.scala:176) 
    at scala.reflect.internal.util.Collections$class.map2(Collections.scala:55) 
    at scala.reflect.internal.SymbolTable.map2(SymbolTable.scala:14) 
    at scala.reflect.internal.Variances$class.inArgs$1(Variances.scala:176) 
    at scala.reflect.internal.Variances$class.inType$1(Variances.scala:189) 
    at scala.reflect.internal.Variances$$anonfun$inArgs$1$1.apply(Variances.scala:176) 
    at scala.reflect.internal.Variances$$anonfun$inArgs$1$1.apply(Variances.scala:176) 
    at scala.reflect.internal.util.Collections$class.map2(Collections.scala:55) 
    at scala.reflect.internal.SymbolTable.map2(SymbolTable.scala:14) 

at scala.tools.nsc.typechecker.Analyzer$typerFactory$$anon$3.run(Analyzer.scala:93) 
at scala.tools.nsc.Global$Run.compileUnitsInternal(Global.scala:1603) 
at scala.tools.nsc.Global$Run.compileUnits(Global.scala:1588) 
at scala.tools.nsc.Global$Run.compileSources(Global.scala:1583) 
at scala.tools.nsc.interpreter.IMain.compileSourcesKeepingRun(IMain.scala:387) 
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.compileAndSaveRun(IMain.scala:816) 
at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.compile(IMain.scala:775) 
at scala.tools.nsc.interpreter.IMain$Request.compile$lzycompute(IMain.scala:951) 
at scala.tools.nsc.interpreter.IMain$Request.compile(IMain.scala:946) 
at scala.tools.nsc.interpreter.IMain.compile(IMain.scala:530) 
at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:518) 
at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:516) 
at scala.tools.nsc.interpreter.ILoop.reallyInterpret$1(ILoop.scala:748) 
at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:793) 
at scala.tools.nsc.interpreter.ILoop.command(ILoop.scala:660) 
at scala.tools.nsc.interpreter.ILoop.processLine(ILoop.scala:427) 
at scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:444) 
at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply$mcZ$sp(ILoop.scala:862) 
at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:848) 
at scala.tools.nsc.interpreter.ILoop$$anonfun$process$1.apply(ILoop.scala:848) 
at scala.reflect.internal.util.ScalaClassLoader$.savingContextLoader(ScalaClassLoader.scala:95) 
at scala.tools.nsc.interpreter.ILoop.process(ILoop.scala:848) 
at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:81) 
at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:94) 
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:103) 
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala) 

That entry seems to have slain the compiler. Shall I replay 
your session? I can re-run each line except the last one. 
+0

然後,您應該粘貼您的編輯作爲答案並關閉問題,因此顯然問題已解決。 –

回答

0

我發現了問題 - 我原本val recursiveOpt = fields.filter(f => isCaseClassOpt(f.typeSignature)).map(f => f.typeSignature.asInstanceOf[TypeRef].args.head),這意味着,當我在recursiveOpt領域稱爲field.name我名字回來了。

相關問題