haskell在处理复杂程序流时有多懒

ff29svar  于 2022-11-14  发布在  其他
关注(0)|答案(1)|浏览(163)

我在想Haskell有多聪明/懒惰,我能一直确保Haskell只会做生成某个输出所必需的事情吗?

rxztt3cl

rxztt3cl1#

编号

Haskell为它的核心lambda类演算指定了一个指称语义,您可以依赖这个语义。此外,有一个元理论证明,一个特定的归约顺序--通俗地称为“懒惰求值”--实现了这个语义;许多人用它作为他们的思维模型来理解Haskell程序的行为。
Haskell程序可能会以两大类方式进行不必要的评估:

  • Haskell实现可能会选择执行更多的求值。GHC在大多数情况下使用惰性求值,但我相信在某些情况下,它会使用其他求值顺序来提高效率。您也可以查看Eager Haskell项目,它尝试使用另一种实现策略。原则上,一个实现将在其权利范围内选择推测性地将一些计算分叉到另一个线程(如果不需要的话,就把结果扔掉)等等--这个主题有很多可能的变化。
  • 指定的指称语义可能需要比“必要”更多的评估。例如,一个偶尔会绊倒初学者的语义:
primes :: [Int]
  primes = 2 : filter prime [3,5..]

  prime :: Int -> Bool
  prime x = and [x `mod` p /= 0 | p <- primes, p < x]

当检查3是否应该在列表primes中时,实际上没有必要检查primes2之后的任何元素,因为序列是严格单调递增的,但是 haskell 并没有(也没有试图)聪明到注意到这一点;它将直接尝试检查primes的其余部分,并以无限循环结束,而不是给出素数列表。
一个更小的例子:您可以认为x && False始终为False,但通常无论如何都会计算x,因为语义表明如果x为,则这应该是一个无限循环(对比False && x,它通常不会导致计算x)。
也就是说,当你说“复杂结构”时,脑海中浮现的一件事是:Haskell做懒惰的事情吗 * 即使是我定义的自定义数据类型 *?也就是说,像散列Map、平衡树和k-d树等复杂的结构会被懒惰地处理吗?答案是 * 是的 *,至少对于GHC是这样;除了IO之外,Prelude中的任何类型基本上都没有什么特别之处。列表、布尔值、Maybe等是惰性的,这并不是因为编译器知道关于它们的特殊情况,而仅仅是Haskell指定的惰性求值简化规则及其作为类型的声明的结果。
当然,有很多方法可以让你选择放弃懒惰,你会在Hackage上看到的一些结构可以通过各种方式做到这一点;但不用担心,通常会在文档中声明。

相关问题