为什么GHC不对常量进行优化?

13
import Data.List
a = foldl' (+) 0 [1..99999999]
main = putStrLn $ show $ a

这个程序运行起来需要一些时间。但是 a 不依赖于任何东西,因此它是恒定的。它可以在编译时完美地计算出来。为什么 GHC 没有针对这个进行优化呢?是否有一个标志来使其进行优化,还是我应该直接用这些值代替常量计算?


1
a并不是完全的常量,因为它是多态的。 GHC 推断类型为 (Num a, Enum a) => a,在内部将被翻译成需要类字典参数的函数。 (但即使你将其设为 a :: Int,该值也只会在运行时计算。) - leftaroundabout
4
你所谈论的被称为超级编译,它并不像你表现得那么简单。这是一个研究领域,我相信一些人正在使用Haskell进行测试,但它还远未准备好投入生产。你可能可以通过一些Template Haskell的技巧来使用它,但这通常是不建议的。 - kqr
2个回答

13

这不是一个完美的解决方案,但正如kqr已经指出的那样,你当然可以通过模板Haskell实现你的目标:

{-# LANGUAGE TemplateHaskell #-}

import Language.Haskell.TH
import Data.List

a :: Integer
a = $( return . LitE . IntegerL $ foldl' (+) 0 [1..99999999] )

main = print a

在实际开始编译程序之前,这个折叠表达式生成整数字面量4999999950000000


7

这与reddit帖子中讨论的内容相同。基本上,您希望的是一种在实践中很少出现的简单情况。使用常量折叠进行优化很快就会涉及到哥德尔不完备定理和停机问题。


5
为什么不能有一个指示“在编译时计算”的Pragma,这样证明终止性的责任就在程序员身上了? - Gabriella Gonzalez
4
我不明白它如何适用。我只是想要一个编译器指示,告诉ghc尝试在编译时计算值。如果它没有终止,那么我可以接受。 - Gabriella Gonzalez
5
没问题。如果我不知道它是否会终止,我也不介意。就像我说的,担心终止的负担在程序员身上,我很乐意等待直到我等得不耐烦。 - Gabriella Gonzalez
4
我不知道我是否正确,但没关系。就像我说的一样,让我自己担心这个问题吧。毕竟,Haskell 已经有了 UndecidableInstances 可以将可决定性的责任转移到程序员身上,所以这有什么区别呢?只需要让程序员去担心它是否是可决定的。 - Gabriella Gonzalez
2
人工提供的注释是一种常见的策略,用于向编译器提供关于个别特殊情况的事实,这些情况在一般情况下无法自动推断。unsafePerformIO和它的朋友们实际上就是这样;通过使用它们,您断言可以执行特定的IO操作,就像它是一个纯函数一样,并且如果您错了,造成的灾难就是您的问题。Mercury的同类版本明确称为“承诺”,甚至还有一个关于终止分析的承诺。 - Ben
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接