使用注释+宏或外部脚本生成 Scala 代码?
Scala code generation with annotations + macros or external script?
我想知道:
- Scala annotations/transforms 可以实现下面的代码生成吗? (objective)
- 与使用外部工具生成源代码相比,有哪些取舍? (objective)
- 有没有更好的方法/你会怎么做? (主观)
背景:
我正在为 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 转换实际上可以生成所需的代码,也许需要一点帮助,例如让用户定义对象以及模型定义。我说的对吗?
这个想法我遇到的一些问题:
- 该对象将充满无用的属性,它所需要的只是 Column 对象。这可以通过将 class 拆分为两个 class 来解决,PersonMeta 和 Person 扩展 PersonMeta,Person 对象仅扩展 PersonMeta。
- IDEs 可能不会选择生成的属性,导致它们用波浪线下划线(eww...)并使其自动完成 属性 names won工作。代码仍然会在编译时检查,所以它实际上只是一个 IDE 陷阱(动态,毫无疑问,有同样的问题。)
使用脚本生成代码更 IDE 友好,但它很麻烦,可能需要更多工作,尤其是因为您必须保持自定义方法和事物完好无损。它还需要一个自定义构建步骤,每当您更改模型时都必须 运行(这意味着您可能会忘记这样做。)虽然 IDE 可能无法帮助您生成宏代码(但是,无论如何)如果你做错了,编译器会冲你大喊大叫。这让我倾向于使用宏 + 注释来完成它。
你怎么看?我是 Scala 的新手,我有点怀疑我是否找到了定义模型并为它们生成实现的最佳方法。你会怎么做更好?
有可能是的。宏的编写和调试可能令人不快,但它们确实有效。
看来您已经有了解决方案
Scala IDE 倾向于正确处理宏(我的意思是,它们必须如此,它们是语言的一部分,并在一些非常基础的库中使用),所以我不会担心那;如果有的话,宏比外部代码生成步骤更 ide 友好,因为宏将与用户的更改保持同步。
在求助于宏之前,我会看看您是否可以在 vanilla scala 中实现您想要的。请记住,您的 class 之类的东西不一定是实际情况 class;查看 Shapeless generic records 中的 idea,了解如何从外部某处表示类型良好的 "row" 命名值。我认为一个可以将像这些记录这样的结构映射到 SQL 和从 SQL 映射的系统最终可能比基于 "special" 案例 class 的系统更具原则性(即更容易推理) es.
我想知道:
- Scala annotations/transforms 可以实现下面的代码生成吗? (objective)
- 与使用外部工具生成源代码相比,有哪些取舍? (objective)
- 有没有更好的方法/你会怎么做? (主观)
背景:
我正在为 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 转换实际上可以生成所需的代码,也许需要一点帮助,例如让用户定义对象以及模型定义。我说的对吗?
这个想法我遇到的一些问题:
- 该对象将充满无用的属性,它所需要的只是 Column 对象。这可以通过将 class 拆分为两个 class 来解决,PersonMeta 和 Person 扩展 PersonMeta,Person 对象仅扩展 PersonMeta。
- IDEs 可能不会选择生成的属性,导致它们用波浪线下划线(eww...)并使其自动完成 属性 names won工作。代码仍然会在编译时检查,所以它实际上只是一个 IDE 陷阱(动态,毫无疑问,有同样的问题。)
使用脚本生成代码更 IDE 友好,但它很麻烦,可能需要更多工作,尤其是因为您必须保持自定义方法和事物完好无损。它还需要一个自定义构建步骤,每当您更改模型时都必须 运行(这意味着您可能会忘记这样做。)虽然 IDE 可能无法帮助您生成宏代码(但是,无论如何)如果你做错了,编译器会冲你大喊大叫。这让我倾向于使用宏 + 注释来完成它。
你怎么看?我是 Scala 的新手,我有点怀疑我是否找到了定义模型并为它们生成实现的最佳方法。你会怎么做更好?
有可能是的。宏的编写和调试可能令人不快,但它们确实有效。
看来您已经有了解决方案
Scala IDE 倾向于正确处理宏(我的意思是,它们必须如此,它们是语言的一部分,并在一些非常基础的库中使用),所以我不会担心那;如果有的话,宏比外部代码生成步骤更 ide 友好,因为宏将与用户的更改保持同步。
在求助于宏之前,我会看看您是否可以在 vanilla scala 中实现您想要的。请记住,您的 class 之类的东西不一定是实际情况 class;查看 Shapeless generic records 中的 idea,了解如何从外部某处表示类型良好的 "row" 命名值。我认为一个可以将像这些记录这样的结构映射到 SQL 和从 SQL 映射的系统最终可能比基于 "special" 案例 class 的系统更具原则性(即更容易推理) es.