为什么我不能用seq强制执行IO操作?

Bak*_*riu 8 io haskell seq

鉴于此代码段:

someFunction x = print x `seq` 1

main = do print (someFunction "test")
Run Code Online (Sandbox Code Playgroud)

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

$./seq_test 
1
Run Code Online (Sandbox Code Playgroud)

如果我替换它error我可以检查左操作数seq 是否确实被评估.

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

test
1
Run Code Online (Sandbox Code Playgroud)

只修改someFunction

lef*_*out 11

评估一个IO动作什么都不做.那就对了!

如果您愿意,IO类型的值只是"指令列表".因此,所有你与该做的seq是强制程序,以确保1什么应该,如果是真正的动作来完成使用.使用动作与评估无关,它意味着单独将其与main呼叫绑定.但是,正如你所说,因为这someFunction是一个非monadic签名的函数,这在这里不可能发生.

你可以做什么...... 但不要

import Foreign

someFunction x = unsafePerformIO (print x) `seq` 1
Run Code Online (Sandbox Code Playgroud)

这实际上将评估与IO执行相结合.这通常在Haskell中是一个非常糟糕的主意,因为评估可能发生在完全不可预测的顺序上,可能发生的次数可能比你想象的要多(因为编译器假设参考透明度),以及其他混乱场景.

正确的解决方案是将签名更改为monadic:

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

main = do
     y <- someFunction "test"
     print y
Run Code Online (Sandbox Code Playgroud)

1事实上,即使没有,程序也尽可能确定seq.只有通过执行操作才能获得更多细节.

  • 是的,这是正确的和有意的.喜欢它还是恨它.(同样,还有`Debug.Trace`,用于调试 - 除了调试之外的任何其他事情,确实和在纯函数中实现输出的任何其他方式一样不安全.) - 说真的,如果你写惯用的Haskell你会对于这种临时日志记录而言,感觉远不如其他语言. (3认同)
  • 如果要添加print语句以更好地理解一些复杂代码的执行方式,那就像调试一样.您可以使用Debug.Trace在随机位置获取输出,当您理解代码时,再次删除这些调用. (2认同)
  • 如果解析期间的输出是一个要求(听起来像一个奇怪的要求;你应该能够交错解析和打印),那么你的函数必须有IO类型. (2认同)