Скажем, что у меня много подобных классов данных. Вот пример класса User, который определяется следующим образом:
case class User (name: String, age: Int, posts: List[String]) {
  val numPosts: Int = posts.length
  ...
  def foo = "bar"
  ...
}
Я заинтересован в автоматическом создании метода (во время компиляции), который возвращает Map таким образом, что каждое имя поля сопоставляется с его значением, когда оно вызывается во время выполнения. В приведенном выше примере предположим, что мой метод называется toMap:
val myUser = User("Foo", 25, List("Lorem", "Ipsum"))
myUser.toMap
должен возвращать
Map("name" -> "Foo", "age" -> 25, "posts" -> List("Lorem", "Ipsum"), "numPosts" -> 2)
Как бы вы сделали это с помощью макросов?
Вот что я сделал: во-первых, я создал класс Model в качестве суперкласса для всех моих классов данных и внедрил такой метод:
abstract class Model {
  def toMap[T]: Map[String, Any] = macro toMap_impl[T]
}
class User(...) extends Model {
  ...
}
Затем я определил реализацию макроса в отдельном объекте Macros:
object Macros {
  import scala.language.experimental.macros
  import scala.reflect.macros.Context
  def getMap_impl[T: c.WeakTypeTag](c: Context): c.Expr[Map[String, Any]] = {
    import c.universe._
    val tpe = weakTypeOf[T]
    // Filter members that start with "value", which are val fields
    val members = tpe.members.toList.filter(m => !m.isMethod && m.toString.startsWith("value"))
    // Create ("fieldName", field) tuples to construct a map from field names to fields themselves
    val tuples =
      for {
        m <- members
        val fieldString = Literal(Constant(m.toString.replace("value ", "")))
        val field = Ident(m)
      } yield (fieldString, field)
    val mappings = tuples.toMap
    /* Parse the string version of the map [i.e. Map("posts" -> (posts), "age" -> (age), "name" -> (name))] to get the AST
     * for the map, which is generated as:
     * 
     * Apply(Ident(newTermName("Map")), 
     *   List(
     *     Apply(Select(Literal(Constant("posts")), newTermName("$minus$greater")), List(Ident(newTermName("posts")))), 
     *     Apply(Select(Literal(Constant("age")), newTermName("$minus$greater")), List(Ident(newTermName("age")))), 
     *     Apply(Select(Literal(Constant("name")), newTermName("$minus$greater")), List(Ident(newTermName("name"))))
     *   )
     * )
     * 
     * which is equivalent to Map("posts".$minus$greater(posts), "age".$minus$greater(age), "name".$minus$greater(name)) 
     */
    c.Expr[Map[String, Any]](c.parse(mappings.toString))
  }
}
Но я получаю эту ошибку от sbt, когда пытаюсь ее скомпилировать:
[error] /Users/emre/workspace/DynamoReflection/core/src/main/scala/dynamo/Main.scala:9: not found: value posts
[error]     foo.getMap[User]
[error]               ^
Сначала выполняется сбор макросов. scala. Вот фрагмент из моего Build.scala:
lazy val root: Project = Project(
    "root",
    file("core"),
    settings = buildSettings
  ) aggregate(macros, core)
  lazy val macros: Project = Project(
    "macros",
    file("macros"),
    settings = buildSettings ++ Seq(
      libraryDependencies <+= (scalaVersion)("org.scala-lang" % "scala-reflect" % _))
  )
  lazy val core: Project = Project(
    "core",
    file("core"),
    settings = buildSettings
  ) dependsOn(macros)
Что я делаю неправильно? Я думаю, что компилятор также пытается оценить идентификаторы полей, когда он создает выражение, но я не знаю, как правильно их вернуть в выражении. Не могли бы вы показать мне, как это сделать?
Спасибо очень заблаговременно.