带有类型化属性的脏模板
Dirty templates with typed attributes
我使用 nim 已经有一段时间了,但经常让我感到困惑的是模板。我希望它们 - 至少在签名级别 - 像 procs 一样工作,但它们缺少某些功能(例如可选/默认参数 - 昨天真的让我感到困惑)
无论如何,我有我认为使用模板的足够简单的模式,所以这里有一些代码:
template Kitten*(name: string, age, body: untyped) {.dirty.} =
var kitty_name = name # my {.dirty.} exposes this
echo("Hi, " & name)
if age != 0: echo("wow, " & $age & " years old already!")
body
echo("Bye, " & name)
template Kitten*(name: string, body: untyped) {.dirty.} =
Kitten(name, 0, body)
Kitten("Jimmy"):
echo("It's really nice to meet you, " & kitty_name)
## Ralph and Jimmy cannot co-exist - it's fine, I understand the issue here
# Kitten("Ralph", 5):
# echo("Great that you joined us, " & kitty_name)
编译正确,运行良好。因为我的模板是脏的,所以 kitty_name
可以从 body
中获得。取消注释 Ralph 并注释掉 Jimmy,这也能正常工作。
然后,我意识到 age
没有关联的类型。我当然不想那样——我真傻!所以我修复它:
template Kitten*(name: string, age:int, body: untyped) {.dirty.}=
突然,Jimmy 无法编译。 Ralph 很好 - Ralph 直接使用模板,但是因为 Jimmy 使用覆盖(如果该术语甚至适用于模板?)方法,突然就像主要的 Kitten 关闭了它的边界?还不够脏?
所以问题是,它为什么有效,为什么失败,是错误还是被误解的功能?还是我只是误用了模板?
(p.s。在 0.17.0 和最新的开发分支上试过这个)
所以,事实证明这不是一个错误。
将其添加到问题跟踪器中,并收到了 Andreas 本人的以下回复:
Overloads of templates need to agree on the positions of the untyped parameters. This is documented in the manual.
(我找到的最接近的手册条目在这里 nim manual's "Lazy type resolution for untyped" section,我读得越多,我就越不认为它适用!)
仍在努力解决这个问题;我已经尝试了一些更简单的示例,但我认为这不是全部解释 - 除非结论是 "So mixing untyped and typed causes undefined behaviour - including dropping the dirty pragma" 这并没有让我充满信心。
总有一天,我会深入研究代码并更好地解释它。目前,这是一个悬而未决的问题。
我认为这是一个可以解决的编译器错误,但我会提供对当前行为的解释。
这里的关键是在 Kitten
的调用点,涉及注入的 kitty_name
变量的块无法进行类型检查(由于引用了不存在的变量)。它们必须作为原始 AST 传递,这通过使用模板的 untyped
参数来指示。一旦您引入另一个不在同一位置使用 untyped
参数的重载,编译器将尝试在重载解析期间对传入的块进行类型检查,您将在调用站点收到错误(不是像您假设的那样在模板扩展之后)。
编译器本可以使用几个额外的标准来避免错误 - 例如,它可以根据参数的数量排除其中一个重载。这就是为什么我认为这个问题将来可能会得到解决。
要解决此问题,您可以重命名这两个模板,以免它们超载:
template AgedKitten*(name: string, age: int, body: untyped) {.dirty.} =
block:
var kitty_name = name # my {.dirty.} exposes this
echo("Hi, " & name)
if age != 0: echo("wow, " & $age & " years old already!")
body
echo("Bye, " & name)
template Kitten*(name: string, body: untyped) {.dirty.} =
AgedKitten(name, 0, body)
Kitten("Jimmy"):
echo("It's really nice to meet you, " & kitty_name)
AgedKitten("Ralph", 5):
echo("Great that you joined us, " & kitty_name)
过去,我还对类型化参数和非类型化参数之间的区别进行了更深入的解释,您可能也会觉得有用:
我使用 nim 已经有一段时间了,但经常让我感到困惑的是模板。我希望它们 - 至少在签名级别 - 像 procs 一样工作,但它们缺少某些功能(例如可选/默认参数 - 昨天真的让我感到困惑)
无论如何,我有我认为使用模板的足够简单的模式,所以这里有一些代码:
template Kitten*(name: string, age, body: untyped) {.dirty.} =
var kitty_name = name # my {.dirty.} exposes this
echo("Hi, " & name)
if age != 0: echo("wow, " & $age & " years old already!")
body
echo("Bye, " & name)
template Kitten*(name: string, body: untyped) {.dirty.} =
Kitten(name, 0, body)
Kitten("Jimmy"):
echo("It's really nice to meet you, " & kitty_name)
## Ralph and Jimmy cannot co-exist - it's fine, I understand the issue here
# Kitten("Ralph", 5):
# echo("Great that you joined us, " & kitty_name)
编译正确,运行良好。因为我的模板是脏的,所以 kitty_name
可以从 body
中获得。取消注释 Ralph 并注释掉 Jimmy,这也能正常工作。
然后,我意识到 age
没有关联的类型。我当然不想那样——我真傻!所以我修复它:
template Kitten*(name: string, age:int, body: untyped) {.dirty.}=
突然,Jimmy 无法编译。 Ralph 很好 - Ralph 直接使用模板,但是因为 Jimmy 使用覆盖(如果该术语甚至适用于模板?)方法,突然就像主要的 Kitten 关闭了它的边界?还不够脏?
所以问题是,它为什么有效,为什么失败,是错误还是被误解的功能?还是我只是误用了模板?
(p.s。在 0.17.0 和最新的开发分支上试过这个)
所以,事实证明这不是一个错误。
将其添加到问题跟踪器中,并收到了 Andreas 本人的以下回复:
Overloads of templates need to agree on the positions of the untyped parameters. This is documented in the manual.
(我找到的最接近的手册条目在这里 nim manual's "Lazy type resolution for untyped" section,我读得越多,我就越不认为它适用!)
仍在努力解决这个问题;我已经尝试了一些更简单的示例,但我认为这不是全部解释 - 除非结论是 "So mixing untyped and typed causes undefined behaviour - including dropping the dirty pragma" 这并没有让我充满信心。
总有一天,我会深入研究代码并更好地解释它。目前,这是一个悬而未决的问题。
我认为这是一个可以解决的编译器错误,但我会提供对当前行为的解释。
这里的关键是在 Kitten
的调用点,涉及注入的 kitty_name
变量的块无法进行类型检查(由于引用了不存在的变量)。它们必须作为原始 AST 传递,这通过使用模板的 untyped
参数来指示。一旦您引入另一个不在同一位置使用 untyped
参数的重载,编译器将尝试在重载解析期间对传入的块进行类型检查,您将在调用站点收到错误(不是像您假设的那样在模板扩展之后)。
编译器本可以使用几个额外的标准来避免错误 - 例如,它可以根据参数的数量排除其中一个重载。这就是为什么我认为这个问题将来可能会得到解决。
要解决此问题,您可以重命名这两个模板,以免它们超载:
template AgedKitten*(name: string, age: int, body: untyped) {.dirty.} =
block:
var kitty_name = name # my {.dirty.} exposes this
echo("Hi, " & name)
if age != 0: echo("wow, " & $age & " years old already!")
body
echo("Bye, " & name)
template Kitten*(name: string, body: untyped) {.dirty.} =
AgedKitten(name, 0, body)
Kitten("Jimmy"):
echo("It's really nice to meet you, " & kitty_name)
AgedKitten("Ralph", 5):
echo("Great that you joined us, " & kitty_name)
过去,我还对类型化参数和非类型化参数之间的区别进行了更深入的解释,您可能也会觉得有用: