2015-04-20 1 views
2

У меня есть макрос, который анализирует приложения вложенных функций. Это соответствует приложениям и получить типы параметров таким образом:Обработка параметров by-name в макросе Scala

case q"$f[..$targs](..$args)(...$otherArgs)" => 

    // retrieve the list of all parameter types 
    val paramTpes = f.tpe match { 
     case pmt: PolyType if pmt.paramLists.size == 0 => 
     Seq() 
     case pmt: PolyType => 
     pmt.paramLists(0) map {_.typeSignature 
      .substituteTypes(pmt.typeParams, targs map (_.tpe))} 
     case pmt: MethodType if pmt.paramLists.size == 0 => 
     Seq() 
     case pmt: MethodType => 
     pmt.paramLists(0) map (_.typeSignature) 
    } 

Теперь, если случится быть по-именем параметров, что я получаю некоторый странный тип, который печатает => T, но не может быть сопоставлен ни с чем. Кажется, что в API отражения нет никакого средства для правильной обработки. То, что я хотел бы сделать, это получить T в случае, если это '=> T, потому что => T вызывает дополнительные проблемы в сгенерированном коде.

ответ

4

Обычно используют экстракторы. Иногда мне приходится видеть, что такое класс времени выполнения, чтобы помнить, какой экстрактор использовать. Я не использую API каждый день.

scala> import reflect.runtime._, universe._ 
import reflect.runtime._ 
import universe._ 

scala> class X { def x(i: => Int) = i * 2 } 
defined class X 

scala> typeOf[X].member(TermName("x")) 
res0: reflect.runtime.universe.Symbol = method x 

scala> .typeSignature 
res1: reflect.runtime.universe.Type = (i: => scala.Int)scala.Int 

scala> res1 match { case MethodType(ps, res) => ps } 
res2: List[reflect.runtime.universe.Symbol] = List(value i) 

scala> .head 
res3: reflect.runtime.universe.Symbol = value i 

scala> .typeSignature 
res4: reflect.runtime.universe.Type = => scala.Int 

scala> res4.getClass 
res5: Class[_ <: reflect.runtime.universe.Type] = class scala.reflect.internal.Types$ClassArgsTypeRef 

scala> res4 match { case TypeRef(pre, sym, args) => sym } 
res6: reflect.runtime.universe.Symbol = class <byname> 

scala> res4 match { case TypeRef(pre, sym, args) => args } 
res7: List[reflect.runtime.universe.Type] = List(scala.Int) 

scala> definitions 
res8: reflect.runtime.universe.DefinitionsApi = [email protected] 

scala> definitions.By 
ByNameParamClass ByteClass ByteTpe 

scala> definitions.ByNameParamClass 
res9: reflect.runtime.universe.ClassSymbol = class <byname> 

Я смутно помню специальное имя, но могу ли я получить стабильный префикс для сопоставления с образцом? Наверное, нет.

scala> res4 match { case TypeRef(pre, definitions.ByNameParamClass, args) => args } 
<console>:20: error: stable identifier required, but scala.reflect.runtime.`package`.universe.definitions.ByNameParamClass found. 
Note that method ByNameParamClass is not stable because its type, => reflect.runtime.universe.ClassSymbol, is volatile. 
       res4 match { case TypeRef(pre, definitions.ByNameParamClass, args) => args } 
                 ^

scala> val k = definitions.ByNameParamClass 
k: reflect.runtime.universe.ClassSymbol = class <byname> 

scala> res4 match { case TypeRef(pre, k, args) => args } 
res11: List[reflect.runtime.universe.Type] = List(scala.Int) 

scala> 
+0

Спасибо, он работал с 'c.universe.definitions.ByNameParamClass'. Я был близок к тому, чтобы найти решение, поправляя реплику, но не совсем там! –