import Data.List
a = foldl' (+) 0 [1..99999999]
main = putStrLn $ show $ a
这个程序运行起来需要一些时间。但是 a
不依赖于任何东西,因此它是恒定的。它可以在编译时完美地计算出来。为什么 GHC 没有针对这个进行优化呢?是否有一个标志来使其进行优化,还是我应该直接用这些值代替常量计算?
import Data.List
a = foldl' (+) 0 [1..99999999]
main = putStrLn $ show $ a
这个程序运行起来需要一些时间。但是 a
不依赖于任何东西,因此它是恒定的。它可以在编译时完美地计算出来。为什么 GHC 没有针对这个进行优化呢?是否有一个标志来使其进行优化,还是我应该直接用这些值代替常量计算?
这不是一个完美的解决方案,但正如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
。
这与reddit帖子中讨论的内容相同。基本上,您希望的是一种在实践中很少出现的简单情况。使用常量折叠进行优化很快就会涉及到哥德尔不完备定理和停机问题。
ghc
尝试在编译时计算值。如果它没有终止,那么我可以接受。 - Gabriella GonzalezUndecidableInstances
可以将可决定性的责任转移到程序员身上,所以这有什么区别呢?只需要让程序员去担心它是否是可决定的。 - Gabriella GonzalezunsafePerformIO
和它的朋友们实际上就是这样;通过使用它们,您断言可以执行此特定的IO
操作,就像它是一个纯函数一样,并且如果您错了,造成的灾难就是您的问题。Mercury的同类版本明确称为“承诺”,甚至还有一个关于终止分析的承诺。 - Ben
a
并不是完全的常量,因为它是多态的。 GHC 推断类型为(Num a, Enum a) => a
,在内部将被翻译成需要类字典参数的函数。 (但即使你将其设为a :: Int
,该值也只会在运行时计算。) - leftaroundabout