sql - 将宏计算结果传递给运行时的推荐方法是什么?

标签 sql scala macros compilation

我正在尝试构建一些类似于SQL的抽象,但是遇到了问题。

这是一个简化的“数据库表”:

trait Coffee {
  def id: Long
  def name: String
  def brand: String
}

这是我的查询抽象:
import language.experimental.macros

object Query {  
  def from[T] = 
    macro QueryMacros.fromMacro[T]
}

class From[T] {  
  def select[S](s: T => S): Select[T] =
    macro QueryMacros.selectMacro[T, S]
}

class Select[T] {
  def where(pred: T => Boolean): Where =
    macro QueryMacros.whereMacro[T]
}

class Where(val result: String)

这是我的宏实现:
import scala.reflect.macros.Context

object QueryMacros {
  val result = new StringBuilder

  def fromMacro[T : c.WeakTypeTag](c: Context): c.Expr[From[T]] = { 
    result ++= ("FROM " + c.weakTypeOf[T])
    c.universe.reify(new From[T])
  }

  def selectMacro[T : c.WeakTypeTag, S : c.WeakTypeTag](c: Context)(s: c.Expr[T => S]): c.Expr[Select[T]] = {
    result ++= ("SELECT " + s.tree)
    c.universe.reify(new Select[T])
  }

  def whereMacro[S](c: Context)(pred: c.Expr[S]): c.Expr[Where] = {
    result ++= ("WHERE " + pred.tree)
    c.universe.reify(new Where(result.toString))
  }
}

这是我的示例代码:
object Main extends App {
  println("Query start")
  val query = 
    Query.from[Coffee]
         .select(_.id)
         .where(_.brand == "FairTrade")

  println(query.result)
  println("Query end")
}

它可以编译并正常运行,但是输出为:
Query start

Query end

基本上,result似乎是空的。我希望它可以容纳树上积累的绳子。

如何将数据从宏编译阶段传递到下一阶段,以便在运行时显示出来?
我当然可以将当前字符串显式传递给下一个方法,但是我想避免这种情况。

最佳答案

基本上,您需要具有一个Queryable抽象:1)提供集合API(fromselect等),2)通过对调用进行量化并在内部累积调用来记住所调用的方法。

这个概念在我们的ScalaDays幻灯片[1]中有所解释,并在Slick(开源)[2]中实现。顺便说一句,在LINQ中,它们对Queryable上的方法进行的操作大致相同,以重新定义调用并将其馈送到实现IQueryable的对象中,例如如[3]中所述。

链接:

  • http://scalamacros.org/talks/2012-04-18-ScalaDays2012.pdf
  • https://github.com/slick/slick/tree/master/src/main/scala/scala/slick/queryable
  • http://community.bartdesmet.net/blogs/bart/archive/2007/04/06/the-iqueryable-tales-linq-to-ldap-part-1-key-concepts.aspx
  • 关于sql - 将宏计算结果传递给运行时的推荐方法是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11777278/

    相关文章:

    sql - 查询是否存在多个多对多关系

    Scala 函数式接口(interface)特征

    macros - Racket,将列表转换为值

    c++ - 在宏中使用一个参数作为整个 C++ 指令

    java - 获取具有多个状态的时间戳之间的平均值

    mysql - 在 SQL Server 中连接两个表

    mysql - 在 MySQL 中使用自动递增获取最后创建的 ID

    java - 提升 json :Custom serializer for java 8 LocalDateTime throwing mapping exception

    scala - 在 Scala 工作表中使用自定义枚举时,我收到错误 : java. lang.ExceptionInInitializerError

    C 拆分位