为什么在 Go 常量中不允许使用 `math.Sin`?

Why is `math.Sin` disallowed in a Go constant?

根据 Effective Go,函数 math.Sin 不能用于定义常数,因为该函数必须在 运行 时间发生。

此限制背后的原因是什么?浮点一致性? Sin 实现的怪癖?还有别的吗?


其他语言支持此类功能。例如,在 C 中:从版本 4.3 开始,GCC supports compile-time calculation of the sine function。 (参见 "General Optimizer Improvements" 部分)。

但是,如 this blog post by Bruce Dawson 中所述,这可能会导致意外问题。 (参见 "Compile-time versus run-time sin" 部分)。

这是 Go 中的一个相关问题吗?还是由于其他原因限制了这种使用?

Go 不支持用函数的结果初始化常量。函数在运行时调用,而不是在编译时调用。但是常量是在编译时定义的。

可以为某些函数设置例外(例如 math.Sin),但这会使规范更加复杂。 Go 开发人员通常更喜欢保持规范简单和一致。

Go 就是缺乏概念。没有办法将函数标记为纯函数(它的 return 值仅取决于它的参数,并且它不会改变任何类型的可变状态或执行 I/O),也没有办法编译器 推断 纯粹性,并且没有尝试在编译时评估任何包含函数调用的表达式(因为除了常量参数的纯函数之外的任何东西这样做都会是奇怪的来源行为和错误,并且因为添加使其正常工作所需的机制会引入相当多的复杂性)。

是的,这是一个巨大的损失,它迫使在具有不良运行时行为的代码和完全丑陋的代码之间进行权衡。 Go 的支持者会选择丑陋的代码,并告诉你你是一个坏人,因为你不觉得它漂亮。

您可以使用的最好的东西是代码生成。将 go generate 集成到工具链中,并在标准库中提供完整的 Go 解析器,使得在构建时修改代码变得相对容易,并且您可以使用此功能做的事情之一是创建更高级的如果您愿意,可以不断折叠。您仍然会遇到代码生成的所有可调试性风险,但确实如此。