我在想Haskell有多聪明/懒惰,我能一直确保Haskell只会做生成某个输出所必需的事情吗?
rxztt3cl1#
编号
Haskell为它的核心lambda类演算指定了一个指称语义,您可以依赖这个语义。此外,有一个元理论证明,一个特定的归约顺序--通俗地称为“懒惰求值”--实现了这个语义;许多人用它作为他们的思维模型来理解Haskell程序的行为。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中时,实际上没有必要检查primes中2之后的任何元素,因为序列是严格单调递增的,但是 haskell 并没有(也没有试图)聪明到注意到这一点;它将直接尝试检查primes的其余部分,并以无限循环结束,而不是给出素数列表。一个更小的例子:您可以认为x && False始终为False,但通常无论如何都会计算x,因为语义表明如果x为,则这应该是一个无限循环(对比False && x,它通常不会导致计算x)。也就是说,当你说“复杂结构”时,脑海中浮现的一件事是:Haskell做懒惰的事情吗 * 即使是我定义的自定义数据类型 *?也就是说,像散列Map、平衡树和k-d树等复杂的结构会被懒惰地处理吗?答案是 * 是的 *,至少对于GHC是这样;除了IO之外,Prelude中的任何类型基本上都没有什么特别之处。列表、布尔值、Maybe等是惰性的,这并不是因为编译器知道关于它们的特殊情况,而仅仅是Haskell指定的惰性求值简化规则及其作为类型的声明的结果。当然,有很多方法可以让你选择放弃懒惰,你会在Hackage上看到的一些结构可以通过各种方式做到这一点;但不用担心,通常会在文档中声明。
3
primes
2
x && False
False
x
False && x
IO
Prelude
Maybe
1条答案
按热度按时间rxztt3cl1#
编号
Haskell为它的核心lambda类演算指定了一个指称语义,您可以依赖这个语义。此外,有一个元理论证明,一个特定的归约顺序--通俗地称为“懒惰求值”--实现了这个语义;许多人用它作为他们的思维模型来理解Haskell程序的行为。
Haskell程序可能会以两大类方式进行不必要的评估:
当检查
3
是否应该在列表primes
中时,实际上没有必要检查primes
中2
之后的任何元素,因为序列是严格单调递增的,但是 haskell 并没有(也没有试图)聪明到注意到这一点;它将直接尝试检查primes
的其余部分,并以无限循环结束,而不是给出素数列表。一个更小的例子:您可以认为
x && False
始终为False
,但通常无论如何都会计算x
,因为语义表明如果x
为,则这应该是一个无限循环(对比False && x
,它通常不会导致计算x
)。也就是说,当你说“复杂结构”时,脑海中浮现的一件事是:Haskell做懒惰的事情吗 * 即使是我定义的自定义数据类型 *?也就是说,像散列Map、平衡树和k-d树等复杂的结构会被懒惰地处理吗?答案是 * 是的 *,至少对于GHC是这样;除了
IO
之外,Prelude
中的任何类型基本上都没有什么特别之处。列表、布尔值、Maybe
等是惰性的,这并不是因为编译器知道关于它们的特殊情况,而仅仅是Haskell指定的惰性求值简化规则及其作为类型的声明的结果。当然,有很多方法可以让你选择放弃懒惰,你会在Hackage上看到的一些结构可以通过各种方式做到这一点;但不用担心,通常会在文档中声明。