haskell - 为什么预定义的 `const` 函数在某些上下文中的行为与等效的 lambda 函数不同?
问题描述
在进行一些共享实验时,我发现预定义const
函数在某些情况下的行为不同。
f :: (() -> Int) -> Int
f g = g () + g ()
x1 = f (const (trace "x1" 42))
x2 = f (\_ -> (trace "x2" 42))
x3 = f (myconst (trace "x3" 42))
myconst :: a -> b -> a
myconst x _ = x
当此示例在没有优化的情况下编译时,评估仅x1
触发一次评估,而对andtrace
进行两次评估。由于 lambda 函数,这是合理的。x2
x3
x2
x1
x2
x2
x3
x3
252
然而,的定义表明const
它是一个普通的函数定义,没有任何编译器注释可以解释差异。因此,该函数myconst
的行为应该相同,但事实并非如此。如何解释这种行为,有没有办法在这方面影响编译器?
解决方案
如果myconst
编译成单独的模块(即使单独的模块是用 编译的-O0
),那么输出是:
x1
x2
x2
x3
252
不同之处在于——在代码中——在单独的模块中-O0
调用会生成代码:myconst
let x' = myconst (trace "x3" 42) in x' + x'
myconst
但是在同一个模块中调用它就像这样:
trace "x4" 42 + trace "x4" 42
编译-O2
完全改变了代码——所有内容都是内联的,并且跟踪被提升到表达式的顶部,所以它们每个只执行一次。
在这方面,您可以清楚地影响编译器,例如通过是否放入myconst
单独的模块,或者 - 正如@leftroundabout 所指出的 - 通过添加各种内联编译指示。
我不认为您可以在这方面可靠地影响编译器,而且我不确定您可以通过研究未优化的编译输出来了解多少真实世界的 GHC 代码。我认为上面的示例清楚地表明,-O0
基于编译的次要方面,生成的代码将以完全任意和矛盾的方式运行,而这些次要方面您并不认为重要。
推荐阅读
- kotlin - 加载 .txt 文件 tornadoFx Kotlin
- excel - 根据 Power Query 中传感器读数之间的差距对日期时间事件进行分组
- c - 如何只保存我需要的某些字节而不是全部保存在数组中?
- rust - 为什么引入另一个范围会使我的 winit 窗口崩溃?
- vue.js - 无法使用 extendTheme 在 NUXT js 中覆盖 Chakra UI 的主题
- group-policy - 在 C++ 中隐藏凭据提供程序
- ajax - 通过复选框搜索 laravel eloquent
- cassandra - JanusGraph 上的 ConfiguredGraphFactory.open() 返回 Cassandra DriverTimeoutException
- javascript - 使用 react-native-draggable-flatlist 时在 react-native 中出现未定义的错误
- python - 在 PyProj 的全新 Conda 安装中:pyproj 无法设置数据库路径。_pyproj_global_context_initialize()