Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to have a Map[String,Any] with the name and the values of named parameters in Scala?

Tags:

scala

I am writing a wrapper to a REST web service and I'd like to have strongly typed Scala APIs.

The following is what I am doing so far:

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

This approach is working, but it requires me to manually generate the parameters map. If I were able to access to a Map representing parameters and their values, what I am doing would be much cleaner.

like image 981
mariosangiorgio Avatar asked Nov 11 '12 17:11

mariosangiorgio


People also ask

How do you add a value to a map in Scala?

Add Elements to a Map Using the Assignment Operator in Scala We need to pass the key into the constructor and assign a value using the assignment operator. We can also use the += operator to avoid using a constructor and directly add the element to the Map.

How do you access map elements in Scala?

Scala Map get() method with exampleThe get() method is utilized to give the value associated with the keys of the map. The values are returned here as an Option i.e, either in form of Some or None. Return Type: It returns the keys corresponding to the values given in the method as argument.

How is map implemented in Scala?

Per the Scaladoc, “implements immutable maps using a list-based data structure.” As shown in the examples, elements that are added are prepended to the head of the list. Keys of the map are returned in sorted order. Therefore, all traversal methods (such as foreach) return keys in that order.

Are maps mutable in Scala?

Maps are classified into two types: mutable and immutable. By default Scala uses immutable Map. In order to use mutable Map, we must import scala.


1 Answers

You could do this with runtime reflection, and I'm sure you'll get answers telling you how, if you want that, but this is actually a neat use case for Scala 2.10's macros, so here goes. First assume we have a file named 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))
  }
}

I'll leave snake casing the map keys as an (easy) exercise for the reader.

We also have a test file (named Test.scala):

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

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

Now we compile both of these:

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

And when we run Test we'll get the following:

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

The nifty thing about this is that there's none of the overhead of runtime reflection. If we compile the test file with -Ymacro-debug-verbose, we see that the following code has been generated (in effect) for the body of foo at compile time:

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

Exactly as we'd expect.

like image 152
Travis Brown Avatar answered Oct 20 '22 00:10

Travis Brown