使用参照透明度来预先计算haskell中的值

时间:2012-04-10 17:54:40

标签: haskell referential-transparency precompute

假设我们有一个这样的程序:

list = [1..10000000]

main = print $ sum list

我希望对它进行编译,使得可执行文件只打印50000005000000,而不需要花费太多时间和精力。

基本上,任何可以确定计算的表达式(也许严格性分析可以在这里帮助)都可以在编译期间预先计算(即我们使用参考透明度来说它不是真的我们计算价值的时候很重要。)

简而言之:“必须计算”+参考 - 透明度=可以预先计算

这就像运行程序,直到我们点击依赖于输入的东西(即程序的核心,在所有输入中共同的,将被预先计算)

目前是否存在实现此目的的机制(使用Haskell或任何其他语言)? [请不要在C ++中指向模板之类的东西,因为它首先没有参考透明度。]

如果没有,这个问题有多难? [伴随的技术(和理论)问题是什么?]

3 个答案:

答案 0 :(得分:11)

进行编译时计算的一般目的是使用Template Haskell。但对于这个特定用例,您可以使用vector包和LLVM后端,GHC将优化此总和。

sorghum:~% cat >test.hs
import Data.Vector.Unboxed as V
main = print (V.sum $ V.enumFromN (1 :: Int) 10000000)
sorghum:~% ghc --make -O2 -fllvm test.hs
[1 of 1] Compiling Main             ( test.hs, test.o )
Linking test ...
sorghum:~% time ./test
50000005000000
./test  0.00s user 0.00s system 0% cpu 0.002 total
sorghum:~% objdump -d test | grep 2d7988896b40
  40653d:   48 bb 40 6b 89 88 79    movabs $0x2d7988896b40,%rbx
  406547:   49 be 40 6b 89 88 79    movabs $0x2d7988896b40,%r14

(如果不是很明显,0x2d79888896b4050000005000000。)

答案 1 :(得分:11)

一般情况下这不安全。原因是Haskell表达式可能是,但它们也可能无法终止。编译器应该总是终止,所以你能做的最好就是“评估这个结果的1000步”。 1 但是如果你确实添加了这样的限制,那么如果你正在编译一个程序来运行怎么办?在具有太字节RAM的超级计算机集群上,编译器内存不足?

您可以添加许多限制,但最终您会将优化减少为缓慢形式的常量折叠(尤其对于大多数计算依赖于运行时用户输入的程序而言)。由于sum [1..10000000]在这里需要半秒钟,因此它不太可能达到任何合理的限制。

当然,像这样的特定情况通常可以被优化掉,而GHC通常会优化这样复杂的表达式。但是编译器不能只是在编译时安全地评估任何表达式;它必须非常受限制,并且可以说它在这些限制下会有多大帮助。它是一个编译器,而不是一个解释器:)

1 这会大大减慢 包含大量无限循环的任何程序的编译速度 - 由于Haskell不严格,因此比你可能认为)。或者,更常见的是,任何包含大量长时间运行计算的程序。

答案 2 :(得分:1)

听起来像是超级编译的工作!这个问题听起来像是对它的描述,而对非终结的讨论反映了超级编译器开发人员面临的问题。我在GHC维基上看到有人正在为它制作一个生产超级编译器,但不会发生这种情况。