使用注释+宏或外部脚本生成 Scala 代码?

Scala code generation with annotations + macros or external script?

我想知道:

  1. Scala annotations/transforms 可以实现下面的代码生成吗? (objective)
  2. 与使用外部工具生成源代码相比,有哪些取舍? (objective)
  3. 有没有更好的方法/你会怎么做? (主观)

背景:

我正在为 Scala + Java 设计一个嵌入式数据库作为副项目。尽管我希望它可以从 Java 开始使用,但我还是在 Scala 中编写它以获得额外的 flexibility/power,因为编写 Java 代码会扼杀我的灵魂。

我正在研究我想要的模型定义。我最初的想法是解析一些模型定义文件并生成 Scala classes。但我想现在我希望它们可以在 Scala 代码中定义,这样就不需要解析,人们可以利用 Scala 的全部功能来定义和自定义模型(例如自定义列类型)。这是来自我的 Python/Django 经历。

到目前为止我有类似的东西:

@model
class Person {
  val Name = StringColumn(length=32)
  val BirthDate = DateColumn(optional=true)
}

@model
class Student extends Person {
  val GPA = FloatColumn(propertyName="gpa")
}

@model
class Teacher extends Person {
  val salary = NumericColumn()
}

这会产生:

class Person {
  val Name = StringColumn(name="name", length=32)
  val BirthDate = DateColumn(name="birthDate", optional=true)

  // generated accessor methods
  def name = Person.Name.get(...)
  def name_= (name : String) : Unit = Person.Name.set(..., name)
  // etc ...
}

// static access to model metadata, e.g. Person.Name is an immutable StringColumn instance
object Person extends Person

class Student extends Person {
  val GPA = DoubleColumn(name = "GPA")

  def gpa = ...
  def gpa_= (value : Float) = ...
}

object Student extends Student

class Teacher extends Person {
  // You get the idea
}

object Teacher extends Teacher

查看一些在线示例并进行一些研究,似乎使用特殊 @model 注释的 AST 转换实际上可以生成所需的代码,也许需要一点帮助,例如让用户定义对象以及模型定义。我说的对吗?

这个想法我遇到的一些问题:

  1. 该对象将充满无用的属性,它所需要的只是 Column 对象。这可以通过将 class 拆分为两个 class 来解决,PersonMeta 和 Person 扩展 PersonMeta,Person 对象仅扩展 PersonMeta。
  2. IDEs 可能不会选择生成的属性,导致它们用波浪线下划线(eww...)并使其自动完成 属性 names won工作。代码仍然会在编译时检查,所以它实际上只是一个 IDE 陷阱(动态,毫无疑问,有同样的问题。)

使用脚本生成代码更 IDE 友好,但它很麻烦,可能需要更多工作,尤其是因为您必须保持自定义方法和事物完好无损。它还需要一个自定义构建步骤,每当您更改模型时都必须 运行(这意味着您可能会忘记这样做。)虽然 IDE 可能无法帮助您生成宏代码(但是,无论如何)如果你做错了,编译器会冲你大喊大叫。这让我倾向于使用宏 + 注释来完成它。

你怎么看?我是 Scala 的新手,我有点怀疑我是否找到了定义模型并为它们生成实现的最佳方法。你会怎么做更好?

有可能是的。宏的编写和调试可能令人不快,但它们确实有效。

  1. 看来您已经有了解决方案

  2. Scala IDE 倾向于正确处理宏(我的意思是,它们必须如此,它们是语言的一部分,并在一些非常基础的库中使用),所以我不会担心那;如果有的话,宏比外部代码生成步骤更 ide 友好,因为宏将与用户的更改保持同步。

在求助于宏之前,我会看看您是否可以在 vanilla scala 中实现您想要的。请记住,您的 class 之类的东西不一定是实际情况 class;查看 Shapeless generic records 中的 idea,了解如何从外部某处表示类型良好的 "row" 命名值。我认为一个可以将像这些记录这样的结构映射到 SQL 和从 SQL 映射的系统最终可能比基于 "special" 案例 class 的系统更具原则性(即更容易推理) es.