为什么toInteger :: Int - >整数是懒惰的吗?

时间:2010-05-01 12:57:43

标签: haskell lazy-evaluation

我有以下代码:

{-# NOINLINE i2i #-}
i2i :: Int -> Integer
i2i x = toInteger x

main = print $ i2i 2

使用-ddump-simpl标志运行GHC会给出:

[Arity 1
 NoCafRefs
 Str: DmdType U(L)]
Main.i2i = GHC.Real.toInteger1

似乎从Int到Integer的转换是懒惰的。为什么会这样 - 有可能我有

的情况
(toInteger _|_ ::Int) /= _|_

编辑:这个问题更多地与GHC严格性分析器有关,而不是与懒惰本身有关。此代码源于探索标准均值函数:

--mean :: Integer -> Integer -> [Integer] -> Double
mean :: Integer -> Int -> [Integer] -> Double
mean acc n [] = fromIntegral acc / fromIntegral n
mean acc n (x:xs) = mean (acc + x) (n + 1) xs

main = print $ mean 0 0 [1..1000000]

此代码在O(N)空间上运行。当我取消注释第一行时,空间消耗将更改为O(1)。似乎它归结为整体调用,而这又归结为toInteger。严格分析器不知何故无法推断转换是严格的,这对我来说似乎很奇怪。

2 个答案:

答案 0 :(得分:13)

对编辑的回应:O(N)空间泄漏对于累积参数的危险是众所周知的,至少对Haskell程序员来说是这样。什么应该是众所周知的,但不是因为 语言,你应该永远不要相信优化器为程序的空间和时间行为提供渐近保证。< / strong>我不明白我自己编写的简单优化器的含义,更不用说像GHC的前端那样毛茸茸的东西,使用严格分析器,内联器和其他所有东西。

关于你的两个问题,

  

为什么GHC的严格性分析器在 优化非常相似的代码时不优化这个特定的代码?

谁知道? (也许Simon PJ知道,也许不是。)如果你关心性能,你不应该依赖严格性分析器。这带来了第二个隐含的问题:

  

如何避免此函数和使用累积参数的所有其他函数的O(N)空间成本?

通过在累积参数上加上严格注释,强制在每次尾递归调用时对它们进行求值。

答案 1 :(得分:2)

我认为你正在以错误的方式看待这个问题。考虑以下,愚蠢的代码片段

let x = [undefined]
let y = map toInteger x

如果我们评估

 y == []

我们得到False,而如果我们评估

 head y

我们收到undefined个例外。没有理由将mapy[]进行比较,因为x的唯一元素未定义。这是非严格性的本质。