背景:我正在用 Swift 为一种玩具语言编写一个编译器,并且我想要一种优雅的方式来为我的语言创建 AST。我的玩具语言中的语句的 AST 类型如下所示:
indirect enum Statement {
case assignment(variable: String, expression: Expression)
case conditional(`if`: Expression, then: Statement, `else`: Statement)
case loop(`while`: Expression, `do`: Statement)
case sequence(Statement, Statement)
case noop
case halt
}
Run Code Online (Sandbox Code Playgroud)
现在,如果我想为一系列语句编写 AST,我必须编写:
// let's say I want to represent a series of 4 no-ops:
.sequence(.noop,
.sequence(.noop,
.sequence(.noop,
.noop)))
Run Code Online (Sandbox Code Playgroud)
这看起来非常冗长。我想如果我能使用这个功能就好了@resultBuilder,这样我就可以写:
Statement {
.noop
.noop
.noop
.noop
}
Run Code Online (Sandbox Code Playgroud)
这是我的尝试:
@resultBuilder
struct StatementBuilder {
static func buildBlock(_ components: Statement...) -> Statement {
if components.isEmpty {
return .noop
} else {
return components.dropFirst().reduce(components.first!) { x, y in .sequence(x, y) }
}
}
}
extension Statement {
init(@StatementBuilder block: () -> Statement) {
self = block()
}
}
Run Code Online (Sandbox Code Playgroud)
但是,这给了我错误:
无法推断引用成员“noop”的上下文基础
在Statement { ... }街区里。
上下文基础有哪些不清楚的地方?除了 之外,它还可以是什么类型Statement?我可以通过在所有内容前面加上 前缀来解决这个问题Statement.,但这太冗长了。我还可以做些什么?
请注意,我还计划重载运算符,以便可以轻松创建赋值和表达式,使语法树类型符合ExpressibleXXXLiteral,并添加If和While函数采用StatementBuilders,从而创建.conditional和.loop语句。因此,结果生成器将比仅仅用于创建无操作和停止有用得多。
ResultBuilder 表达式似乎使用了一种称为单向约束的东西
是的,这是预期的行为。函数生成器中表达式的推断类型不会影响其他表达式的推断类型。
与 Swift 中的大多数表达式不同,函数构建器使用一种称为单向约束的东西来进行类型推断(这就是为什么对 buildBlock 的常规函数调用与类型推断的行为不同)。单向约束意味着类型信息只能在一个方向上流动,而不是通常的双向类型推断。因此,在这种情况下,类型信息 Conv2D 将影响模型推断的整体类型,但不会影响 Sequential { .... 内的其他表达式。
@Douglas_Gregor 写了一篇关于单向约束的解释以及为什么在他的 PR 中使用它们并添加了它们: https ://github.com/apple/swift/pull/25983
| 归档时间: |
|
| 查看次数: |
376 次 |
| 最近记录: |