【问题标题】:Why :sprint always prints a "_"?为什么 :sprint 总是打印一个“_”?
【发布时间】:2026-02-17 00:25:01
【问题描述】:
Prelude> let a = 3
Prelude> :sprint a
a = _
Prelude> let c = "ab"
Prelude> :sprint c
c = _

为什么它总是打印_?我不太明白:sprint 命令的语义。

【问题讨论】:

  • try seq c () after: :sprint c 会给你c = 'a' : _ ...另请阅读我在你上一个问题中发布的链接

标签: debugging haskell lazy-evaluation ghci


【解决方案1】:

Haskell 是一种惰性语言。在“需要”结果之前,它不会评估结果。

现在,只需 打印 一个值就会导致所有的值都是“需要的”。换句话说,如果你在 GHCi 中键入一个表达式,它会尝试打印出结果,这会导致它全部被计算。通常这就是你想要的。

sprint 命令(它是 GHCi 功能,不是 Haskell 语言的一部分)允许您查看此时已评估了多少值。

例如:

Prelude> let xs = [1..]
Prelude> :sprint xs
xs = _

所以,我们刚刚声明了xs,目前尚未对其进行评估。现在让我们打印出第一个元素:

Prelude> head xs
1
Prelude> :sprint xs
xs = 1 : _

现在 GHCi 已经评估了榜单的头部,但仅此而已。

Prelude> take 10 xs
[1,2,3,4,5,6,7,8,9,10]
Prelude> :sprint xs
xs = 1 : 2 : 3 : 4 : 5 : 6 : 7 : 8 : 9 : 10 : _

现在评估了前 10 个元素,但还剩下更多。 (因为xs 是一个无限列表,这并不奇怪。)

您可以构造其他表达式并一次评估它们以查看发生了什么。这实际上是 GHCi 调试器的一部分,它可以让您一次一点地单步执行您的代码。特别是如果你的代码陷入无限循环,你不想print 任何东西,因为这可能会锁定 GHCi。但是你仍然想看看发生了什么......因此sprint,它可以让你看到到目前为止评估的内容。

【讨论】:

  • 备注:由于[1..] 具有多态类型,因此:sp xs 很可能产生_,即使在head xs 之后也是如此。 let xs = [1..] :: [Integer] 会解决这个问题。
【解决方案2】:

我有点晚了,但我遇到了类似的问题:

λ: let xs = [1,2,3]
xs :: Num t => [t]
λ: :sprint xs
xs = _
λ: print xs
λ: :sprint xs
xs = _

此问题特定于多态值。如果您启用了-XNoMonomorphismRestriction,ghci 将永远不会真正评估/强制 xs,它只会评估/强制专业化:

λ: :set -XMonomorphismRestriction
λ: let xs = [1,2,3]
xs :: [Integer]
λ: print xs
λ: :sprint xs
xs = [1,2,3]

【讨论】:

  • 请注意,这只适用于像xs这样的“重载”值;具有变量类型并且对这些变量具有类型类约束的事物。主要是涉及以这种方式表现的文字数字的示例;不幸的是,在学习过程中,数字是显而易见的事情。如果您改为使用布尔值(TrueFalse&&and 等),那么无论NoMonomorphismRestriction 或您是否给出显式类型签名,所有内容的行为大多相同。
【解决方案3】:

Haskell 很懒惰。它在需要之前不会评估事物。

GHCi sprint 命令(不是 Haskell 的一部分,只是解释器的调试命令)打印表达式的值而不强制求值。

当你写作时

let a = 3

您将新名称a 绑定到右侧表达式,但 Haskell 还不会评估那个东西。因此,当您sprint 它时,它会打印_ 作为值,以指示表达式尚未被计算。

试试这个:

let a = 3
:sprint a -- a has not been evaluated yet
print a -- forces evaluation of a
:sprint a -- now a has been evaluated

【讨论】:

  • 对于let a = 3,它更复杂(你应该试试这个) - 默认情况下a 将是a :: Num a => a 并且在你的情况下将始终:sprint_ - IMO @987654332 @example 更适合感受这个
  • 如果你使用let a = 3,你最好指定一个类型let a = 3 :: Int,这样你就不会感到困惑了,还要注意在GHC 7.8中这种行为在旧版本中发生了变化,你不必编写输入此语句以使 :sprint 按“预期”工作。
最近更新 更多