2013-06-30 58 views
6

下面是一個明顯的可變參數函數:是否可以使用可變參數定義一個宏,並獲取每個參數的類型?

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

我們可以定義以類似的方式宏:

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

fun(1,"1",1.0) 

但是,在這種情況下,所有參數都是類型爲Any。事實上,編譯器知道編譯時的類型,但隱藏它。是否有可能在宏中獲得其參數的列表?

回答

6

當然,例如:

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

見我的問題here和bug報告here更多的討論。

相關問題