2012-11-11 20 views
6

Tôi đang viết một trình bao bọc cho một dịch vụ web REST và tôi muốn có các API Scala được đánh mạnh mẽ.Có thể có một Map [String, Any] với tên và các giá trị của các tham số có tên trong Scala không?

Sau đây là những gì tôi đang làm cho đến nay:

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. 
*/ 
... 
} 

Cách tiếp cận này đang làm việc, nhưng nó đòi hỏi tôi phải tự tạo ra bản đồ parameters. Nếu tôi có thể truy cập vào một Bản đồ đại diện cho các thông số và giá trị của chúng, những gì tôi đang làm sẽ sạch hơn nhiều.

Trả lời

11

Bạn có thể làm điều này với phản xạ thời gian chạy và tôi chắc chắn bạn sẽ nhận được câu trả lời cho bạn biết cách thực hiện, nếu bạn muốn, nhưng đây thực sự là trường hợp sử dụng gọn gàng cho Scala 2.10's macros. Trước tiên, chúng tôi có một tệp có tên là 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)) 
    } 
} 

Tôi sẽ để con rắn tạo khóa bản đồ như một bài tập dễ (dễ) cho người đọc.

Chúng tôi cũng có một tập tin thử nghiệm (tên Test.scala):

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

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

Bây giờ chúng ta biên dịch cả hai:

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

Và khi chúng tôi chạy Test chúng tôi sẽ nhận được như sau:

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

Điều tiện lợi về điều này là không có chi phí nào của r không phản ánh. Nếu chúng ta biên dịch các tập tin thử nghiệm với -Ymacro-debug-verbose, chúng ta thấy rằng mã sau đây đã được tạo ra (có hiệu lực) cho cơ thể của foo tại thời gian biên dịch:

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

Đúng như chúng ta mong muốn.

+1

Bạn có biết liệu có thể sử dụng nó với sbt không? – mariosangiorgio

+3

Tôi đã tìm ra. Nó chỉ đơn giản là yêu cầu thay đổi phiên bản scala 'scalaVersion: =" 2.10.0-RC2 "' và nhập khẩu ' ngôn ngữ nhập khẩu.experimental.macros' trong các tệp bằng tính năng – mariosangiorgio

Các vấn đề liên quan