2014-01-18 3 views
1

편집 내 스칼라 호출 할 때 "MatchError AnyRef"나는이 문제를 해결했습니다 - 내가 잘못 field.namecopy 나에게 잘못된 필드 이름을주는 것을 의미 recursiveOpt.map(f => f.typeSignature.asInstanceOf[TypeRef].args.head)를 호출했습니다 방법. map을 삭제했으며 현재 모든 것이 작동합니다.A를 컴파일러 충돌 : 나는 매크로


저는 사례 클래스에 대한 업데이트 방법의지도를 만드는 매크로를 작성하고 있습니다.

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

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

처럼 호출 할 것입니다

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))))}) 

같은이 외부에 대한 업데이 트 맵을 생성한다 생각하는 JSON의 KV 쌍 주어, 내가 할 수있는 그 키를 사용하여 맵에서 적절한 업데이트 메소드를 검색 한 다음 암시 적 변환을 사용하여 값을 사용하여 업데이트를 적용하여 json 값을 적절한 유형으로 변환합니다.

내 매크로는 플랫 케이스 클래스의 경우에 사용됩니다. Inner(innerStr: String) 및 중첩 된 케이스 클래스의 경우. Outer(outerStr: String, inner: Inner). 그러나 중첩 된 옵션 사례 클래스 인 Outer(outerStr: String, innerOpt: Option[Inner])의 경우 컴파일러가 충돌합니다. 비참하게 뭔가 잘못하고 있는지, 아니면 컴파일러에 버그가 있는지 세 번째 옵션인지 잘 모르겠습니다. 이는 스칼라 2.11.0-M7 REPL 아래

를 사용하여 수행되었다 내 코드입니다 - 나는 내 REPL로 플레이 프레임 워크를 가져올 필요가 없도록 String 입력 대신 JsValue 입력을 받아들이는 Map을 구성하고있어 . blacklist은 업데이트 맵에 포함되어서는 안되는 필드를 걸러냅니다 (예 : 우리가 적용 할 케이스 클래스 중 하나는 "crypted_password"및 "salt"와 같은 필드를 가지며 REST 라우트를 통해 전송 된 json을 통해 업데이트되지 않아야 함) . baseMethods은 플랫 케이스의 key -> 메소드 튜플을 구성하고 recursiveMethods은 중첩 된 케이스의 키 - 메소드 튜플을 구성하며 recursiveOptMethods은 중첩 된 옵션 케이스의 키 - 값 튜플을 구성합니다. 매크로의 맨 아래에 이들은 모두 편평한 순서로 합쳐져 있으며 Map에 있습니다.

recursiveOptMethods quasiquotes의 코드를 테스트하여 올바르게 형식화 된 튜플 시퀀스를 만들고 오류가 발견되지 않았는지 확인했습니다 (또한이 코드는 작동하는 recursiveMethods quasiquotes와 매우 유사합니다). 정확하게) 그리고 나는 base, recursiverecursiveOpt 기호 시퀀스를 구성하는 코드를 테스트했으며 그 코드가 자신의 작업을 수행하고있는 것으로 보입니다.

내가 왜 컴파일러를 크래시하고 있는지에 대한 도움은 크게 감사하겠습니다.

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에서 내 오류의 상단과 하단


.내가 문제가 발견

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

(OuterOption[Inner] 필드가) 0-M7 REPL copyTest[Outer]()를 호출 - 원래 내가 의미하는 val recursiveOpt = fields.filter(f => isCaseClassOpt(f.typeSignature)).map(f => f.typeSignature.asInstanceOf[TypeRef].args.head)을했다가 그 나는 recursiveOpt 필드에 field.name라고했을 때 잘못된 이름을 되찾고 있었다.

관련 문제