Можно ли определить макрос с переменными параметрами и получить тип для каждого параметра?

Ниже приведена очевидная вариационная функция:

def fun(xs: Any*) = ???

Мы можем определить макрос аналогичным образом:

def funImpl(c: Context)(xs: c.Expr[Any]*) = ???

fun(1,"1",1.0)

Но в этом случае все аргументы имеют тип Any. На самом деле компилятор знает типы во время компиляции, но скрывает это от нас. Можно ли получить список аргументов и их типов в макросе?


person Rogach    schedule 30.06.2013    source источник


Ответы (1)


Конечно, например:

import scala.language.experimental.macros
import scala.reflect.macros.Context

object Demo {
  def at(xs: Any*)(i: Int) = macro at_impl
  def at_impl(c: Context)(xs: c.Expr[Any]*)(i: c.Expr[Int]) = {
    import c.universe._

    // First let's show that we can recover the types:
    println(xs.map(_.actualType))

    i.tree match {
      case Literal(Constant(index: Int)) => xs.lift(index).getOrElse(
        c.abort(c.enclosingPosition, "Invalid index!")
      )
      case _ => c.abort(c.enclosingPosition, "Need a literal index!")
    }
  }
}

А потом:

scala> Demo.at(1, 'b, "c", 'd')(1)
List(Int(1), Symbol, String("c"), Char('d'))
res0: Symbol = 'b

scala> Demo.at(1, 'b, "c", 'd')(2)
List(Int(1), Symbol, String("c"), Char('d'))
res1: String = c

Обратите внимание, что предполагаемые типы являются точными и правильными.

Обратите также внимание, что это не сработает, если аргумент представляет собой последовательность с атрибутом типа _*, и что вам нужно будет написать что-то вроде следующего, если вы хотите отловить этот случай и предоставить полезное сообщение об ошибке:

def at_impl(c: Context)(xs: c.Expr[Any]*)(i: c.Expr[Int]) = {
  import c.universe._

  xs.toList.map(_.tree) match {
    case Typed(_, Ident(tpnme.WILDCARD_STAR)) :: Nil => 
      c.abort(c.enclosingPosition, "Needs real varargs!")
    case _ =>
      i.tree match {
        case Literal(Constant(index: Int)) => xs.lift(index).getOrElse(
          c.abort(c.enclosingPosition, "Invalid index!")
        )
        case _ => c.abort(c.enclosingPosition, "Need a literal index!")
      }
  }
}

См. мой вопрос здесь и отчет об ошибке здесь для дальнейшего обсуждения.

person Travis Brown    schedule 30.06.2013