편집 내 스칼라 호출 할 때 "MatchError AnyRef"나는이 문제를 해결했습니다 - 내가 잘못 field.name
내 copy
나에게 잘못된 필드 이름을주는 것을 의미 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
, recursive
및 recursiveOpt
기호 시퀀스를 구성하는 코드를 테스트했으며 그 코드가 자신의 작업을 수행하고있는 것으로 보입니다.
내가 왜 컴파일러를 크래시하고 있는지에 대한 도움은 크게 감사하겠습니다.
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.
그런 다음 편집을 답안으로 붙여넣고 질문을 닫아야 문제가 해결 되었음이 분명합니다. –