2015-02-03 3 views
2

У меня есть макрос с подписью какКак заменить фактические аргументы типа в общем методе для получения окончательных типов аргументов значения?

def generateSomething[A] = macro ... 

То есть, он принимает параметр типа. Тип, как ожидается, будет классом case, поэтому он всегда имеет соответствующий метод apply в своем сопутствующем объекте.

Этот макрос, среди всего прочего, генерирует вызов этого apply метода, так что, к примеру, для этого класса:

case class A(x: Int, y: String) 

следующий вызов будет сгенерирован:

A.apply(someFunction[Int], someFunction[String]) 

I извлечение параметров параметров для someFunction звонков с apply подпись.

Все нормально, если не A спараметрирован:

case class A[T](x: Int, y: T) 

С моим текущим подходом генерируется для generateSomething[A[String]] следующее:

A.apply[String](someFunction[Int], someFunction[T]) 

, который, очевидно, не действует.

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

generateSomething[A[String]] 

генерирует

A.apply[String](someFunction[Int], someFunction[String]) 

, а не кусок выше. Является ли это возможным?

Update

Я думаю, что я должен переформулировать вопрос.

Предположим, что существует класс

case class A[T1, ..., Tn](x1: A1, ..., xm: Am) 

, где Ai может зависеть от произвольного подмножества Tk. Примеры:

// T1 = T 
// A1 = Int, A2 = T 
case class B[T](x: Int, y: T) 

// T1 = U, T2 = V 
// A1 = Map[U, V], A2 = List[V] 
case class C[U, V](m: Map[U, V], l: List[V]) 

// T1 = W 
// A1 = W, A2 = W 
case class D[W](t: W, u: W) 

// No Ts 
// A1 = String, A2 = Double 
case class E(v: String, w: Double) // no type parameters at all 

Мне нужно написать макрос, который принимает аргумент типа A и расширяющийся к A.apply вызова метода с предварительно обработаных аргументами:

myMacro[A[U1, ..., Un]] 

// expands to 

A.apply[U1, ..., Un](preprocess[A1], ..., preprocess[An]) 

Uk здесь являются фактические аргументы типа, которые замещены вместо Tk , Например (с использованием классов выше):

myMacro[B[String]] -> B.apply[String](preprocess[Int], preprocess[String]) 

myMacro[C[Int, Double]] -> C.apply[Int, Double](preprocess[Map[Int, Double]], preprocess[List[Double]]) 

myMacro[D[Long]] -> D.apply[Long](preprocess[Long], preprocess[Long]) 

myMacro[E] -> D.apply(preprocess[String], preprocess[Double]) 

Вы видите, apply типы аргументов могут зависеть от параметров типа.Хотя эти параметры известны макросу (поскольку он всегда вызывается с конкретными типами), я не знаю, как «передать» эти параметры «через» до apply, чтобы аргументы типа preprocess были правильными.

Update 2

Here неплотно, что я в настоящее время.

ответ

1

Что-то вроде:

case class X[A](a: A) 

object TParamMacro { 
    import scala.language.experimental.macros 
    import scala.reflect.macros.whitebox.Context 

    def m[A](): A = macro mImpl[A] 

    def mImpl[A: c.WeakTypeTag](c: Context)(): c.Expr[A] = { 
    import c.universe._ 
    val TypeRef(pre, sym, args) = weakTypeTag[A].tpe 
    val t = args.head 
    val expr = 
     if (t <:< typeOf[String]) q"""X.apply[$t]("hi")""" 
     else if (t <:< typeOf[Int]) q"X.apply[$t](42)" 
     else q"X.apply[$t](null)" 
    c.Expr[A](expr) 
    } 
} 

object Test extends App { 
    Console println TParamMacro.m[X[String]]() 
} 

Еще пример:

object TParamMacro { 
    import scala.language.experimental.macros 
    import scala.reflect.macros.whitebox.Context 

    def m[A](): Any = macro mImpl[A] 

    def mImpl[A: c.WeakTypeTag](c: Context)() = { 
    import c.universe._ 
    val TypeRef(pre, sym, args) = weakTypeTag[A].tpe 
    val t = args.head 
    val expr = if (t <:< typeOf[String]) q"""X.apply[List[$t]](List.apply[$t]("hi"))""" 
     else if (t <:< typeOf[Int]) q"X.apply[List[$t]](List.apply[$t](42))" 
     else q"X.apply[List[$t]](Nil)" 
    expr 
    } 
} 

Где

Console println TParamMacro.m[X[String]]() 

дает

X(List(hi)) 

Edit с сутью отремонтированной:

package evaluator 

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

object Evaluator { 
    def preprocess[T]: T = ??? 

    def evaluate[A]: Any = macro evaluateImpl[A] 

    def evaluateImpl[A: c.WeakTypeTag](c: Context): c.Expr[A] = { 
    import c.universe._ 

    val tpe = weakTypeOf[A] 
    val sym = tpe.typeSymbol.asClass 

    require(sym.isCaseClass) 

    val companionSym = sym.companion 
    val companionTpe = companionSym.typeSignature 
    val applyMethod = companionTpe.member(TermName("apply")).asMethod 

    val paramTypes = applyMethod.paramLists.flatten.map(_.typeSignature) 
    Console println s"apply($paramTypes)" 

    val TypeRef(_, _, tpeTypeArgs) = tpe 

    val from = applyMethod.typeParams 
    val to = tpeTypeArgs 
    val arguments = paramTypes map { t => 
     val u = if (from.nonEmpty) t.substituteTypes(from, to) else t 
     Console println s"param is $t, subst is $u" 
     q"evaluator.Evaluator.preprocess[$u]" 
    } 

    c.Expr(q"$companionSym.apply[..$tpeTypeArgs](..$arguments)") 
    } 
} 

Так вы просто подставляя свой «фактические аргументы типа» для параметров типа метода. Полезно использовать «параметр» для формального параметра и «аргумента» для фактического arg в приложении.

Пример:

package evaluator 

case class A(x: Int, y: String) 

case class B[T](x: Int, y: T) 

case class C[U, T](x: Int, y: T, z: U) 

object Test extends App { 
    Evaluator.evaluate[A] 
    Evaluator.evaluate[B[String]] 
    Evaluator.evaluate[C[String, List[Int]]] 
} 

Использование

-Xprint:typer 

затем

A.apply(evaluator.Evaluator.preprocess[Int], evaluator.Evaluator.preprocess[String]); 
B.apply[String](evaluator.Evaluator.preprocess[Int], evaluator.Evaluator.preprocess[String]); 
C.apply[String, List[Int]](evaluator.Evaluator.preprocess[Int], evaluator.Evaluator.preprocess[List[Int]], evaluator.Evaluator.preprocess[String]) 
+0

Извините, но я не понимаю, как это может помочь. См. Мое обновление. Мне нужен общий механизм для обработки классов case с произвольными параметрами типа и произвольным количеством аргументов значений. –

+0

'$ t' - тип arg, показанный в приложении с квазикодированием. Вы можете использовать список аргументов в любом случае. В вашем редактировании замените 'List.apply' на' preprocess', и это пример, который я дал. Или, покажите нам, что вы пробовали, и мы скажем вам, что вы сделали неправильно. –

+0

Ваша программа работает специально для типа 'X', который имеет параметр одного типа и параметр одиночного значения. Мне нужно работать с типами с произвольным количеством типов и параметров типа. Хорошо, я скоро отправлю свой код. –

-1

Scala macro docs обеспечивает некоторое представление:

import scala.reflect.macros.blackbox.Context 

class Impl(val c: Context) { 
    def mono = c.literalUnit 
    def poly[T: c.WeakTypeTag] = c.literal(c.weakTypeOf[T].toString) 
} 

object Macros { 
    def mono = macro Impl.mono 
    def poly[T] = macro Impl.poly[T] 
} 

Метод poly выше называет c.weakTypeOf, который фиксирует информацию стертого типа путем создания неявного во время компиляции. Аналогичная модель может помочь в подтверждении вашего макроса.

EDIT для обновления выше:

c.WeakTypeTag фиксирует информацию о типе на сайте вызова. Компилятор, нуждающийся в выполнении ограничений типа, применяемых к аргументам макроса, применяет неявное преобразование, которое фиксирует эту информацию из ваших типов.

Идея заключалась в том, что как только у вас есть эта информация, вы можете использовать ее для получения строки для неизвестного типа, которую вы можете использовать во время reification.

Что касается дисперсии числа параметров типа, я думаю, что количество параметров для макроса должно быть не зависит от типа, к которому оно применяется, чтобы иметь какой-либо смысл, то есть на ваш последний блок я бы вместо того, чтобы сделать что-то вроде этого (при условии, все они имели два поля в apply):

myMacro[B, Int, String]] -> B.apply(preprocess[Int], preprocess[String]) 

myMacro[C, Map[Int,Double], List[Double]] -> C.apply(preprocess[Map[Int, Double]], preprocess[List[Double]]) 

myMacro[D, Long, Long] -> D.apply(preprocess[Long], preprocess[Long]) 

myMacro[E, String, Double] -> E.apply(preprocess[String], preprocess[Double]) 

Опять же, так как это псевдокод ваш пробег может варьироваться, но применяя некоторые единообразие подписи вашего макроса (и, возможно, применить реализации) вероятно, поможет вам в реализации.

+0

Спасибо, но я не вижу, как это может помочь. Пожалуйста, прочтите мое обновление. –

+0

Спасибо за обновление, но, к сожалению, я не уверен, что понимаю, что вы имеете в виду. 'myMacro' должен работать с произвольными типами с произвольными параметрами типа, и он имеет единственный параметр. Количество аргументов 'apply' не зависит и не зависит от количества параметров типа исходного типа. –

+0

Вам, скорее всего, потребуется реализовать макросы для каждой цели, которую вы хотите поддержать. Это будет большая работа. –

 Смежные вопросы

  • Нет связанных вопросов^_^