为什么(常量)表达式在Haskell中没有在编译时计算?

Dan*_*our 9 optimization performance haskell compile-time-constant compile-time

我目前正在学习Haskell,有一件事令我感到困惑:

当我构建一个复杂的表达式(其计算需要一些时间)并且该表达式是常量(意味着它只构建已知的硬编码值)时,不会在编译时计算表达式.

来自C/C++背景我习惯于这种优化.

在Haskell/GHC中执行此类优化(默认情况下)的原因是什么?有什么好处,如果有的话?

data Tree a =
   EmptyTree
 | Node a (Tree a) (Tree a)
 deriving (Show, Read, Eq)

elementToTree :: a -> Tree a
elementToTree x = Node x EmptyTree EmptyTree

treeInsert :: (Ord a) => a -> Tree a -> Tree a
treeInsert x EmptyTree = elementToTree x
treeInsert x (Node a left right)
  | x == a = Node x left right
  | x < a  = Node a (treeInsert x left) right
  | x > a  = Node a left (treeInsert x right)

treeFromList :: (Ord a) => [a] -> Tree a
treeFromList []     = EmptyTree
treeFromList (x:xs) = treeInsert x (treeFromList xs)

treeElem :: (Ord a) => a -> Tree a -> Bool
treeElem x EmptyTree = False
treeElem x (Node a left right)
  | x == a = True
  | x < a  = treeElem x left
  | x > a  = treeElem x right

main = do
  let tree = treeFromList [0..90000]
  putStrLn $ show (treeElem 3 tree)
Run Code Online (Sandbox Code Playgroud)

由于这将始终打印,True我希望编译的程序几乎立即打印和退出.

Dan*_*ner 15

你可能会喜欢这个reddit线程.编译器可以尝试这样做,但它可能是危险的,因为任何类型的常量都可以做有趣的事情,比如循环.至少有两种解决方案:一种是超级编译,但尚未作为任何编译器的一部分提供,但您可以尝试各种研究人员的原型; 更实际的是使用Template Haskell,这是GHC的机制,让程序员要求在编译时运行一些代码.


bhe*_*ilr 2

在这种情况下,GHC 无法确定计算是否会完成。这不是一个懒惰与严格的问题,而是一个停止的问题。对您来说,说这treeFromlist [0..90000]是一个可以在编译时计算的常量看起来很简单,但是编译器如何知道这一点呢?编译器可以轻松优化[0..90000]为常量,但您甚至不会注意到这种变化。

  • @DanielOertwig 但接下来我们又回到了停止问题。对于像“[1..]”这样的明显情况,你根本无法仅仅添加“例外”。相反,对于每个常量表达式,您必须证明它不是无限的,即计算停止。 (2认同)