2013-06-03 3 views
3

Я пытаюсь обобщить настройку Squeryl (Slick создает те же проблемы AFAIK). Я хочу, чтобы я не указывал каждый класс case явно для ряда общих методов.Scala Макросы, генерирующие вызовы параметров типа

table[Person] 
table[Bookmark] 
etc. 

Это также идет для создания индексов и создания методов обертки методов CRUD для каждого случай класса.

Так в идеале, что я хочу сделать, это список классов и сделать их в виде таблиц, добавить индексы и добавить метод обертку:

val listOfClasses = List(classOf[Person], classOf[Bookmark]) 
listOfClasses.foreach(clazz => { 
    val tbl = table[clazz] 
    tbl.id is indexed 
    etc. 
}) 

Я думал, что Scala Макросы бы вещь, чтобы применить здесь, так как я не думаю, что вы можете иметь значения как параметры типа. Кроме того, мне нужно генерировать методы для каждого типа формы:

def insert(model: Person): Person = persons.insert(model) 

Я получил мой МИТС на примере на макросах, но я не знаю, как создать родовую структуру данных.

Я получил этот простой пример, чтобы проиллюстрировать то, что я хочу:

def makeList_impl(c: Context)(clazz: c.Expr[Class[_]]): c.Expr[Unit] = { 
    import c.universe._ 

    reify { 
    println(List[clazz.splice]()) // ERROR: error: type splice is not a member of c.Expr[Class[_]] 
    } 
} 

def makeList(clazz: Class[_]): Unit = macro makeList_impl 

Как мне это сделать? Или Scala Macros - неправильный инструмент?

ответ

3

К сожалению, reify не является достаточно гибким для вашего случая использования, но есть хорошие новости. В макро-раю (и, скорее всего, в 2.11.0) у нас есть лучший инструмент для построения деревьев, называемый квазикварталами: http://docs.scala-lang.org/overviews/macros/quasiquotes.html.

scala> def makeList_impl(c: Context)(clazz: c.Expr[Class[_]]): c.Expr[Any] = { 
    | import c.universe._ 
    | val ConstantType(Constant(tpe: Type)) = clazz.tree.tpe 
    | c.Expr[Any](q"List[$tpe]()") 
    | } 
makeList_impl: (c: scala.reflect.macros.Context)(clazz: c.Expr[Class[_]])c.Expr[Any] 

scala> def makeList(clazz: Class[_]): Any = macro makeList_impl 
defined term macro makeList: (clazz: Class[_])Any 

scala> makeList(classOf[Int]) 
res2: List[Int] = List() 

scala> makeList(classOf[String]) 
res3: List[String] = List() 

Quasiquotes даже доступен в 2.10.x с небольшой подстройкой к накоплению процессу (http://docs.scala-lang.org/overviews/macros/paradise.html#macro_paradise_for_210x), так что вы можете дать им попробовать.

+0

Aha! Спасибо за ответ. Я пробежал по q.«нотация, но не могла найти никакой документации. Это именно то, что я хочу, чтобы она сделала. Спасибо! –

1

Это, вероятно, не заполнить все ваши потребности здесь, но это может немного помочь:

Сигнатура table метода выглядит следующим образом:

protected def table[T]()(implicit manifestT: Manifest[T]): Table[T] 

Как вы можете видеть, что происходит неявное Manifest объект. Этот объект передается автоматически компилятором и содержит информацию о типе T. Это фактически то, что Squeryl использует для проверки типа сущности базы данных.

Вы можете просто передать эти манифесты явно так:

val listOfManifests = List(manifest[Person], manifest[Bookmark]) 
listOfManifests.foreach(manifest => { 
    val tbl = table()(manifest) 
    tbl.id is indexed 
    etc. 
}) 

К сожалению tbl в этом коде будет иметь тип, похожий на Table[_ <: CommonSupertypeOfAllGivenEntities], что означает, что все операции на нем должны быть агностиком конкретного типа объекта базы данных.

+0

Спасибо за ответ. Однако я использую Squeryl 0.9.6-M1, у этого есть дополнительный неявный аргумент: 'Недостаточно аргументов для таблицы методов: (implicit manifestT: scala.reflect.Manifest [T], implicit ked: org.squeryl.OptionalKeyedEntityDef [T, _]) org.squeryl.Table [T] ' Это было бы улучшением, хотя, как вы отмечаете, это еще не идеально. Я думаю, что макросы дадут ответ там. Если бы я знал, как построить объект параметризованного типа, основанный на переменной. –

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