2012-11-11 1 views
6

저는 REST 웹 서비스에 래퍼를 작성하고 있으며 강력하게 형식화 된 Scala API를 갖고 싶습니다. Scala에서 명명 된 매개 변수의 이름과 값으로 Map [String, Any]을 가질 수 있습니까?

다음

내가 지금까지 뭘 오전입니다 :이 방법이 작동

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에 액세스 할 수 있다면, 내가하고있는 일은 훨씬 더 명확 해집니다.

답변

11

런타임 리플렉션으로이 작업을 수행 할 수 있습니다. 원하는 경우 답변을 얻을 수 있지만 실제로는 Scala 2.10's macros의 깔끔한 사용 사례이므로 여기서 설명합니다. 나는 독자들에게 (쉽게) 연습으로지도 키 케이스 뱀을 떠날거야

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

: 먼저 우리가 ParamMapMaker.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을 실행할 때 우리는 다음을 얻을 것이다 :

우리는 또한 테스트 파일 (Test.scala 이름)가

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

이것에 대한 멋진 점은 r의 오버 헤드가 없다는 것입니다. 미적 반사. 우리가 -Ymacro-debug-verbose 테스트 파일을 컴파일하는 경우, 우리는 다음과 같은 코드는 컴파일 시간에 foo의 몸 (효과) 생성 된 것을 볼 수 :

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

정확히 우리가 예상대로.

+1

sbt와 함께 사용할 수 있는지 알고 계십니까? – mariosangiorgio

+3

나는 그것을 이해했다. 스칼라 버전'scalaVersion : = "2.10.0-RC2"를 변경하고 기능을 사용하는 파일에서 ' import language.experimental.macros'를 가져 오기만하면됩니다. – mariosangiorgio

관련 문제