目前(截至 2020 年 8 月)Rakudo 不会在编译时对函数的返回值进行类型检查;也就是说,它不提供函数满足其返回约束的静态保证。具体来说,以下两个函数都编译为 Raku:
sub get-int(--> Int) { 'bug' }
sub get-int($a --> Int) {
when $a == 5 { 'Rare bug' }
default { 42 }
}
我有两个相关问题:
-
有什么方法可以知道编译时当前进行的类型检查(如果有)? (或者通过某人编写的列表,在文档中的某个位置,或者在 Rakudo 源代码的中心位置)或者它比这更临时?
-
缺乏编译时类型检查是有意的设计决策吗?或者添加更多静态类型检查是有一天会很好的事情,但尚未实现?
(我很熟悉乔纳森的精彩回答Raku 中类型/约束的性能损失?,其中指出“Raku 要求写入程序的类型约束在运行时强制执行在最新的该答案描述了避免类型检查的运行时成本的各种方法,但没有描述在编译时进行的类型检查(如果有的话)(这肯定会避免运行时成本!)。)
目前,在编译时很少进行类型检查;这主要是静态优化器的副作用。今天的检查主要是关于子程序调用,其中:
- 我们可以确定调用的数量,并知道传递的参数数量永远不会匹配
- 我们有文字参数,并且可以看到它们永远不可能与签名匹配
这是静态优化器执行更多内联工作时遗留下来的。如今,它仅在编译时内联本机运算符,并将其余部分留给虚拟机的动态优化器,该优化器在内联方面的能力要强得多,也可以取消内联(允许推测性优化,但也意味着可以恢复原始堆栈跟踪,而静态优化器丢失了此信息)。
在编译时做更多的事情被认为是可取的,但是有一些实际问题需要考虑。
- 引入额外的检查还可能导致之前有效的代码被破坏。考虑一个模块,其代码路径无法通过更严格的编译时检查,但该模块正在从未遇到这种情况的系统中使用。如果它开始无法在较新版本的编译器上进行编译,那么在编译器升级后将无法部署该系统。一般来说,这意味着执行的检查应该随着语言版本的变化而变化。 (注意,这仍然意味着人们在编写代码时应该声明他们正在编写的语言版本。)
- 在编译时进行更多检查将“肯定会避免运行时成本”可能是真的,但推理起来并不简单。托管运行时不能盲目相信在给定的字节码中所做的承诺,因为这可能会导致内存安全违规(从而导致 SIGSEGV 或更糟)。这在 Raku 这样的语言中显然是正确的,其中类型检查的语义是可编程的,但在 JVM、CLR 等上也是如此。 Raku 中与类型相关的最大优势来自于使用本机类型,这可以避免大量分配,从而避免垃圾收集工作。
- 实施进一步的检查将增加编译器的复杂性以及编译所需的时间。第一个已经是一个问题了;编译器前端在大约十年内没有看到任何重大的架构变化。目前为宏奠定基础的 RakuAST 工作还涉及编译器前端的近乎重写。改进的架构应该可以轻松实现进一步的编译时类型检查,但我们还需要考虑如何并行化编译的各个方面,这可以允许编译器在不增加挂钟编译时间的情况下执行更多操作。
一旦当前的编译器前端检修完成,引入更多编译时检查(但仅从下一个语言版本启用)似乎很有可能 - 至少,只要有人致力于它。
然而,这个领域出现了一个更令人兴奋的机会:由于 Raku 程序将有一个 API,并且随着定制编译器通道的计划一起进行,很快就可以实现类型检查器作为模块!其中一些可能会导致检查,将其纳入未来的 Raku 语言版本中。其他的可能是非常特定于领域的,旨在更正确地使用给定的模块。其他人可能会强制执行不符合基本语言精神的严格要求,但某些语言用户可能希望选择加入。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)