6

我正在为 REST Web 服务编写一个包装器,并且我希望拥有强类型的 Scala API。

以下是我目前正在做的事情:

def getMentions(count: Option[Int] = None,
                sinceID: Option[TweetID] = None,
                maxID: Option[TweetID] = None,
                trimUser: Option[Boolean] = None,
                contributorDetails: Option[Boolean] = None,
                includeEntities: Option[Boolean] = None) : List[Tweet] = {
val parameters = Map("count" -> count,
                     "since_id" -> sinceID,
                     "max_id" -> maxID,
                     "trim_user" -> trimUser,
                     "contributor_details" -> contributorDetails,
                     "include_entities" -> includeEntities)
/* 
 * Convert parameters, which is a Map[String,Any] to a Map[String,String]
 * (Removing Nones) and pass it to an object in charge of generating the request.
 */
...
}

这种方法是有效的,但它需要我手动生成parameters地图。如果我能够访问表示参数及其值的 Map,那么我所做的将会更加清晰。

4

1 回答 1

12

你可以通过运行时反射来做到这一点,我相信你会得到告诉你如何做的答案,如果你想要的话,但这实际上是Scala 2.10 的宏的一个很好的用例,所以就这样吧。首先假设我们有一个名为的文件ParamMapMaker.scala

object ParamMapMaker {
  def paramMap: Map[String, Any] = macro paramMapImpl

  def paramMapImpl(c: scala.reflect.macros.Context) = {
    import c.universe._

    val params = c.enclosingMethod match {
      case DefDef(_, _, _, ps :: Nil, _, _) =>
        ps.map(p =>
          reify((
            c.Expr[String](Literal(Constant(p.name.decoded))).splice,
            c.Expr[Any](Ident(p.symbol)).splice
          )).tree
        )
      case _ => c.abort(c.enclosingPosition, "Can't call paramMap here!")
    }

    c.Expr[Map[String, Any]](Apply(Select(Ident("Map"), "apply"), params))
  }
}

我将把蛇形外壳作为对读者的(简单)练习。

我们还有一个测试文件(名为Test.scala):

object Test extends App {
  def foo(hello: String, answer: Int) = ParamMapMaker.paramMap

  println(foo("world", 42))
}

现在我们编译这两个:

scalac -language:experimental.macros ParamMapMaker.scala
scalac Test.scala

当我们运行时,Test我们会得到以下信息:

Map(hello -> world, answer -> 42)

有趣的是,没有运行时反射的开销。如果我们用 编译测试文件,我们会看到在编译时-Ymacro-debug-verbose为 的主体生成了以下代码(实际上) :foo

Map.apply[String, Any](
  scala.Tuple2.apply[String, String]("hello", hello),
  scala.Tuple2.apply[String, Int]("answer", answer)
)

完全符合我们的预期。

于 2012-11-11T21:40:12.040 回答