我有一个使用宏(How to use scala macros to create a function object (to create a Map[String, (T) => T]))构造的case类复制方法的映射,使用隐式转换将输入转换为正在调用的复制方法的正确类型(JsValue
是一个Play框架json值)
case class Clazz(int: Int, string: String)
implicit def jsonToInt(json: JsValue): Int = json.as[Int]
implicit def jsonToStr(json: JsValue): String = json.as[String]
val copyMap: Map[String, (Clazz, JsValue) => Clazz] =
Map("int" -> (c: Clazz, json: JsValue) => c.copy(int = json),
"string" -> (c: Clazz, json: JsValue) => c.copy(string = json))
我想将输入部分应用于从copyMap
检索的函数,例如
val fun: (Clazz, JsValue) => Clazz = copyMap.get("int").get
val newFun: (Clazz) => Clazz = fun(_: Clazz, JsString("non_int_input"))
在这种情况下,当完全应用函数时,该函数在尝试将JsString("non_int_input")
转换为Int
时会抛出异常,但我想在部分应用{时遇到此错误{1}}输入 - 完全应用该函数涉及从数据库中检索JsValue
实例,因此如果字符串输入无效,我宁愿抢占这个。
我是否可以在部分应用的函数中触发/提取任何隐式转换错误?
答案 0 :(得分:0)
我想我可能找到了解决方案
case class Clazz(int: Int, string: String)
implicit def jsonToInt(json: JsValue) = json.as[Int]
implicit def jsonToStr(json: JsValue) = json.as[String]
val copyMap[String, (JsValue) => Try[(Clazz) => Clazz]] =
Map(
"int" -> {
(json: JsValue) => Try[(Clazz) => Clazz] =
Try {
val i: Int = json
(c: Clazz) => c.copy(int = i)
}
},
"string" -> {
(json: JsValue) => Try[(Clazz) => Clazz] =
Try {
val s: String = json
(c: Clazz) => c.copy(string = s)
}
}
)
当我致电时, newFun
将是Failure
val fun: (JsValue) => Try[(Clazz) => Clazz] = copyMap.get("int").get
val newFun: Try[(Clazz) => Clazz] = fun(JsString("non_int_input"))
此代码的宏代码是(我与Try
中的c.universe._
发生导入冲突,因此使用scala.util.Try
)
import play.api.libs.json._
import scala.language.experimental.macros
object Macros {
implicit def jsonToInt(json: JsValue): Int = json.as[Int]
implicit def jsonToIntOpt(json: JsValue): Option[Int] = json.asOpt[Int]
implicit def jsonToStr(json: JsValue): String = json.as[String]
implicit def jsonToStrOpt(json: JsValue): Option[String] = json.asOpt[String]
def copyMap[T]: Map[String, (JsValue) => scala.util.Try[(T) => T]] = macro copyMapImpl[T]
def copyMapImpl[T: c.WeakTypeTag](c: scala.reflect.macros.Context):
c.Expr[Map[String, (JsValue) => scala.util.Try[(T) => T]]] = {
import c.universe._
val tpe = weakTypeOf[T]
val fields = tpe.declarations.collectFirst {
case m: MethodSymbol if m.isPrimaryConstructor => m
}.get.paramss.head
val methods = fields.map {
field => {
val name = field.name
val decoded = name.decoded
val fieldType = field.typeSignature.typeSymbol
val fieldTypeName = fieldType.name.decoded
q"""{$decoded -> {
(json: JsValue) => scala.util.Try {
val x: $fieldType = json
(t: $tpe) => t.copy($name = x)
}.recoverWith {
case e: Exception => scala.util.Failure(
new IllegalArgumentException("Failed to parse " + Json.stringify(json) + " as " + $decoded + ": " + $fieldTypeName)
)
}
}}"""
}
}
c.Expr[Map[String, (JsValue) => scala.util.Try[(T) => T]]] {
q"Map(..$methods)"
}
}
}