【问题标题】:Why can't I force an IO action with seq?为什么我不能使用 seq 强制执行 IO 操作?
【发布时间】:2013-12-02 09:07:24
【问题描述】:

鉴于此代码 sn-p:

someFunction x = print x `seq` 1

main = do print (someFunction "test")

为什么在执行代码时print x 不打印test

$./seq_test 
1

如果我用 error 替换它,我可以检查 seq 的左操作数 确实被评估了。

我怎样才能达到我的预期输出:

test
1

只修改someFunction?

【问题讨论】:

  • 评估一个 IO 操作与执行它不同。没有任何方法可以让 someFunction 做你想做的事,因为如果你想在其中使用 print ,它必须在 IO monad 中,你将无法打印它的结果(不修改 main )。
  • 我想知道为什么投反对票?我相信我的问题是一个完全有效的问题。

标签: haskell io seq


【解决方案1】:

评估IO 操作没有任何作用。没错!

如果您愿意,IO 类型的值仅仅是“指令列表”。因此,您对seq 所做的一切就是强制程序确定1 如果实际使用了该操作应该做什么。并且使用一个动作与评估无关,这意味着将其绑定到 main 调用。但是,正如你所说,someFunction 是一个具有非单子签名的 函数,所以这里不可能发生这种情况。

你能做什么...... 但不能,是

import Foreign

someFunction x = unsafePerformIO (print x) `seq` 1

这实际上将评估与IO 执行结合起来。这在 Haskell 中通常是一个非常糟糕的主意,因为评估可能以完全不可预见的顺序发生,可能与您想象的次数不同(因为编译器假定引用透明​​)以及其他混乱场景。

正确的解决方案是将签名改为单子:

someFunction :: Int -> IO Int
someFunction x = do
     print x
     return 1

main = do
     y <- someFunction "test"
     print y

1碰巧的是,即使没有seq,程序也尽可能确定。更多细节只能通过执行action获得。

【讨论】:

  • 所以你的意思是如果一个库需要一个具有特定非单子签名的函数,那么在 Haskell 中绝对没有办法在其执行期间安全地添加任何输出并继续使用该库?跨度>
  • 是的,这是正确的,也是有意的。喜欢它还是恨它。 (同样,有Debug.Trace,用于调试——但除了调试之外,这确实与在纯函数中实现输出的任何其他方式一样不安全。)——说真的,如果你编写惯用的 Haskell,你会感觉很多与其他语言相比,这种临时日志记录的需要
  • 如果图书馆有关于其一元特性的良好文档,我会同意。例如。 Alex 和 Happy没有 提供好的文档,我什至从一些用户那里听说某些文档实际上是错误的(这比没有文档更糟糕)。如果我对这种输出有要求,我将不得不恢复执行随机更改和猜测实现方法才能使用它们。如果您碰巧知道如何使用它们,请回答我的另一个问题。
  • 如果您添加打印语句以更好地了解某些复杂代码的执行方式,那么这就像调试一样。您可以使用 Debug.Trace 在随机位置获取输出,当您理解代码后,您可以再次删除这些调用。
  • 如果解析期间的输出是一个要求(听起来很奇怪;你应该能够交错解析和打印),那么你的函数必须有 IO 类型。
【解决方案2】:

seq 将表达式评估为弱头范式,它只是最外层的构造函数(或 lambda 应用程序)。表达式 print x 已经在 WHNF 中,所以 seq 不做任何事情。

你可以通过Debug.Trace.trace函数得到你想要的结果。

【讨论】:

  • 出于 IO 目的是否在 WHNF 中并不重要。 — trace 是一个很好的建议——不过,正如模块名称所示,实际上只是为了调试目的。
  • @leftaroundabout 我知道结果并不重要。但这些都是棘手的问题,最好是准确的。
  • @kosmikus 我认为 IO 操作是包装在新类型构造函数中的函数值 - 即,显然是 WHNF
  • @Ingo print 是一个函数,print x 是一个函数调用。任何地方都没有构造函数;)
  • @Ingo 我的评论是关于“表达式 print x 已经在 WHNF 中,所以 seq 没有做任何事情。”这是错误的,因为print x 不在 WHNF 中,即使它肯定可以评估为 WHNF,并且将由 seq 计算。就这样。我们现在可以结束这次讨论了吗?
猜你喜欢
  • 2012-09-24
  • 1970-01-01
  • 1970-01-01
  • 2014-09-06
  • 1970-01-01
  • 2021-04-13
  • 1970-01-01
  • 2013-12-28
  • 1970-01-01
相关资源
最近更新 更多