2012-08-18 5 views
1

Для DSL, я хотел бы быть в состоянии сделать что-то вроде:Можно ли написать макрос scala, возвращаемый тип которого зависит от аргумента?

object Creator { 
    def create[T](s :String) :Foo[T] = macro createImpl[T] 
    def createImpl[T](c :Context)(s :c.Expr[String]) : c.Expr[Foo[T]] = { 
     reify(new Foo[Any]()) 
    } 
} 

Моей проблемы заключается в замене Any в материализовать с чем-то, что будет возвращать правильно параметризованную версию.

(выше я использую строку в качестве аргумента, но в окончательном варианте я планирую использовать объект компаньона класс T в качестве маркеров, чтобы узнать аргумент-тип FUNCTION1 [T, Unit])

ответ

2

Вы нужно: а) написать new Foo[T]() вместо new Foo[Any]() (легкого) б) передать в макро представление типа T, а именно, значение типа AbsTypeTag[T], путем объявления параметра T с использованием контекста, связанный: [T: c.AbsTypeTag].

Вот код, который я тестировал в Scala 2.10.0-M7. Редактировать. В 2.10.0-RC1 AbsTypeTag был переименован в WeakTypeTag. Все остальное о макросах и тегах типа остается неизменным.

Creator.scala:

import language.experimental.macros 
import reflect.macros.Context 
class Foo[T] 
object Creator { 
    def create[T](s: String): Foo[T] = macro createImpl[T] 
    def createImpl[T: c.AbsTypeTag](c: Context)(s: c.Expr[String]): c.Expr[Foo[T]] = { 
    import c.universe._ 
    reify(new Foo[T]()) 
    } 
} 

MacroClient.scala:

object Main extends App { 
    println (Creator.create[Int]("")) 
} 

Обратите внимание, что если опустить параметр типа, вы получите странные ошибки:

scala> Creator.create[Int]("") 
res2: Foo[Int] = [email protected] 

scala> Creator.create("") 
<console>:12: error: macro has not been expanded 
       Creator.create("") 
        ^

You также написать:

(above I use a string argument, but in the final version I plan to use the companion object of class T as a marker to know the argument-type of a Function1[T,Unit])

но если я понял, это звучит неплохо. Вместо того, чтобы писать Creator.create[T](otherArgs), синтаксис вызова будет чем-то вроде Creator.create(T, otherArgs), а не большим преимуществом (если есть). Но вы даже не можете получить последний синтаксис: если class A и object A являются компаньонами, их типы не связаны: первый имеет тип A, второй имеет тип A.type, где A является сопутствующим объектом, а не типом класса A.


Update: как получить Creator create Foo синтаксис для работы и возвращать экземпляр Foo, если у вас есть контроль над Foo. Поскольку вы спрашиваете о аргументе Any типа reify, я предполагаю, что вы спрашиваете о аргументе типа. Это имеет смысл только в том случае, если вы хотите вернуть тип: Creator.create будет T, а не Any; в противном случае вы должны уточнить свой вопрос.

Проблема здесь не имеет ничего общего с макросами. Creator create Foo передает объект Foo в Creator.create, декларация которого должна быть выражена, с учетом Foo.type, типа Foo через выражение типа. Выражения типа в Scala довольно ограничены - например, они не могут использовать отражение. Но, учитывая тип, они могут выбирать своих членов типа.

trait Companion[Class] 
//How to declare a companion 
class Foo 
object Foo extends Companion[Foo] 
/*I'm cheating: an implementation of Companion does not need to be a true Companion. You can add documentation to explain how Companion is supposed to be used. */ 
object Bar extends Companion[Foo] 
//But this is also useful - you can't create your own companion objects for pre-existing types, but you can still create the right instances of Companion: 
object pInt extends Companion[Int] 
object Creator { 
    //T with Companion[S] is needed to workaround a type inference bug (SI-5298) and allow S to be correctly inferred. 
    def create[S, T <: Companion[S]](obj: T with Companion[S]): S = ??? 
} 

Это ограничено, так как вам необходимо изменить объект-компаньон, но я уверен, что вы не можете сделать лучше. Я не знаю пути, в выражении типа (что можно использовать вместо S при объявлении возвращаемого типа create) получения от объекта-компаньона к его ассоциированному типу класса вообще, и я не думаю, что есть один ,

Теперь, изменяя выше использовать макросы прост:

import language.experimental.macros 
import reflect.macros.Context 
class Foo[T] 
object Creator { 
    //T with Companion[S] is needed to workaround a type inference bug (SI-5298) and allow S to be correctly inferred. 
    def create[S, T <: Companion[S]](obj: T with Companion[S]): Foo[S] = macro createImpl[S, T] 
    def createImpl[S: c.AbsTypeTag, T <: Companion[S]: c.AbsTypeTag](c: Context)(obj: c.Expr[T with Companion[S]]): c.Expr[Foo[S]] = { 
    import c.universe._ 
    reify(new Foo[S]()) 
    } 
} 
+0

1.) Я просил аргумент не вводить параметр ... 2.) 'Создатель«Foo»' или 'а Создатель У Foo' есть некоторое преимущество ... (с точки зрения DSL). 3.) классы и их спутники связаны, и в них есть функции для их решения в отражении api. – subsub

+0

Я не понимаю вашу точку зрения 1). В остальном я уверен, что нет общего способа получить синтаксис 'Creator create Foo', а не для произвольного' Foo'. Связь между классом и его компаньоном не может быть использована посредством вывода типа. Я собираюсь опубликовать не общее решение отдельно. – Blaisorblade

+0

В конце я добавил не общее решение в конец вышеприведенного сообщения. – Blaisorblade

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

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