在部分应用的函数中提取隐式转换异常

时间:2013-12-23 16:35:21

标签: scala implicit-conversion implicit

我有一个使用宏(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实例,因此如果字符串输入无效,我宁愿抢占这个。


我是否可以在部分应用的函数中触发/提取任何隐式转换错误?

1 个答案:

答案 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)"
    }
  }
}