了解 :sprint 和列表评估在 haskell 中的工作原理

Understanding how :sprint and list evaluation works in haskell

我正在研究 haskell book,我了解到 :sprint x 用于打印 x 的元素已被求值而元素未被求值(未被表达的元素通过'_')。

书中提供的例子之一,

Prelude> let blah = enumFromTo 'a' 'z' 
Prelude> :sprint blah 
blah = _

Prelude> take 1 blah 
"a" 
Prelude> :sprint blah 
blah = 'a' : _

为了测试不同的输入,我在 GHCi 中做了这个:-

prelude> let b = [1,2,3,4,5]
prelude> :sprint b
b = _
prelude> take 1 b
[1]
prelude> :sprint b
b = _

最后一个命令中 :sprint b 的输出不应该是 b = 1 : _ 因为我们在使用命令 take 1 b 时只评估单个列表项和一个 cons 运算符?但它显示了上面的输出。这是怎么发生的,为什么会发生?输出不应该类似于String类型的输出吗?

编辑:我进行了更多试验并得到了这个结果:-

prelude> let b = [1..10] :: [Int]
prelude> :sprint b
b = _
prelude> take 3 b
[1,2,3]
prelude> :sprint b
b = 1 : 2 : 3 : _

好吧,我最初的猜测是,这是因为我构建这两个列表的方式?一种是使用范围,另一种是通过显式声明其元素(这反过来通过在其元素上递归地使用 cons ':' 构造函数来创建列表)

:sprint 的行为可能有点棘手。在这种情况下,查看 x 的类型:

> :t x
x :: Num t => [t]

因为它是多态的,所以生成的实际值取决于您需要的 Num 的特定实例。因此,x 的行为更像是一个生成列表 [1,2,3,4,5] 的函数,它可以确定您希望元素具有的类型。

现在你可能会想,"ok, I'll make a list that isn't polymorphic",所以你试试这个:

> let x = [1,2,3,4,5 :: Int]
> :t x
x :: [Int]
> :sprint x
x = [1,2,3,4,5]

到底是什么?如果你仔细想想,这是有道理的。我们已经明确地告诉 ghci 列表是什么。这不像它可以取消评估然后稍后重新评估它。 (无论如何这都是浪费。)但是让我们看看当我们尝试将函数映射到 x:

时会发生什么
> let y = map (+1) x
> :sprint y
y = _
> take 1 y
[2]
> :sprint y
y = 2 : _

果然不出所料!

希望对您有所帮助。惰性评估是 Haskell 中比较棘手的事情之一(当它变得重要时)。