【问题标题】:Is it possible to easily build DSL in Kotlin?是否可以在 Kotlin 中轻松构建 DSL?
【发布时间】:2021-06-29 06:22:06
【问题描述】:

TypeScript 允许以非常漂亮、干净和 100% 类型安全的方式构建类似数据的 DSL。我想知道在 Kotlin 中是否有可能?

例如,在下面的 TypeScript 代码 (playground) 中,我们定义了数据表的列。它检查值是否正确(字符串枚举),检查所有可选/必填字段,自动完成等。它开箱即用,您需要做的就是定义类型。

是否可以在 Kotlin 中使用类似的东西? 可以使用 Java Builder-pattern,但并不理想,并且需要为 builder-method 编写大量代码。另外,Kotlin 没有办法使用"number" 枚举,它会是Type.number,看起来不太好。或者也许我遗漏了一些东西,并且有一种方法可以在 Kotlin 中构建漂亮而干净的 DSL,而无需太多样板代码?

// Defining DSL ---------------------------------------------
type Type = "string" | "number" | "boolean" | "unknown"

interface StringFormatOptions {
  type: "string"
}

interface LineFormatOptions {
  type:   "line"
  ticks?: number[]
}

interface Column {
  type:    Type
  format?: StringFormatOptions | LineFormatOptions
}


// Using DSL ------------------------------------------------
const columns: Column[] = [
  {
    type:  "number",
    format: { type:  "line", ticks: [1000] }
  },
  {
    type:  "string"
  }
]

【问题讨论】:

  • 有个不错的项目AutoDSL,但是不支持Kotlin 1.4+

标签: kotlin


【解决方案1】:

是的,您可以在 Kotlin 中创建类型安全的 DSL。一开始可能很难理解,但是习惯了就真的很容易了。

它通过创建接收具有特定接收器类型的 lambdas 的函数来工作......好吧......让我们再试一次。假设您是现有 DSL 的用户,会发生以下情况:

  1. 有一个函数需要为其提供 lambda。
  2. 您提供了一个 lambda。
  3. 该函数为您的 lambda 提供特定类型的 this 参数。
  4. 您可以在 lambda 中使用提供的 this 对象的属性/函数,从而有效地深入 DSL 链。

让我们看看这个例子:

fun copy(init: CopyBuilder.() -> Unit) { TODO() }

interface CopyBuilder {
    var from: String
    var to: String

    fun options(init: CopyOptionsBuilder.() -> Unit) { TODO() }
}

interface CopyOptionsBuilder {
    var copyAttributes: Boolean
    var followSymlinks: Boolean
}

我们有一个接收 lambda 的copy() 函数。提供的 lambda 将可以访问 CopyBuilder 对象作为 this,因此它可以访问例如fromto 属性。通过从 lambda 中调用 options(),我们可以更深入地进行操作,现在我们可以访问 CopyOptionsBuilder 对象。

copy() 负责为您的 lambda 提供 CopyBuilder 对象的正确实现。同样,options() 的实现需要提供CopyOptionsBuilder 的正确实现。上面的示例中省略了这一点。

那么它可以用作:

copy {
    from = "source"
    to = "destination"

    options {
        copyAttributes = true
        followSymlinks = false
    }
}

如果您将 Gradle 与 Kotlin DSL 一起使用,那么 build.gradle.kts 文件实际上是一个常规的 Kotlin 文件。它只是从提供给您的一些变量开始。 Kotlin 中 DSL 的另一个很好的例子是 kotlinx.html 库。它生成具有如下语法的 HTML 代码:

html {
    body {
        div {
            a("https://kotlinlang.org") {
                target = ATarget.blank
                +"Main site"
            }
        }
    }
}

您可以在此处阅读更多信息:https://kotlinlang.org/docs/type-safe-builders.html

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-11-15
    • 2016-12-28
    • 2012-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-31
    • 1970-01-01
    相关资源
    最近更新 更多