`evaluate`、`rwhnf` 和 `seq` 与对应的 "deep" 之间有什么区别?

What are the differences between `evaluate`, `rwhnf` and `seq` and their "deep" counterparts?

the wiki page about timing computations中有一个纯计算计时的例子。核心思想是使用函数 evaluaternfseq 来确保在两次调用 getCPUTime:

time :: (Num t, NFData t) => t -> IO ()
time y = do
    start <- getCPUTime
    replicateM_ lim $ do
        x <- evaluate $ 1 + y
        rnf x `seq` return ()
    end   <- getCPUTime

我想在这里阐明函数evaluaternfseq的用法。

首先,evaluate将其参数求值为弱头范式,但似乎它的另一个目的是处理求值过程中可能发生的异常。我不确定没有它会发生什么,但我准备假设异常处理需要它。

然后,rnf 将值计算为范式,以确保整个计算都在这里进行,并且由于调用 evaluate.正如所讨论的那样,这可能是也可能不是人们想要的 here,因为可能不需要额外的评估。

最后,seq 确保表达式 rnf x 在返回 return () 之前被评估为弱头部范式。

总结一下:evaluate 处理潜在的异常,rnf 深入评估 x 以确保整个计算是定时的,seq 确保 rnf x 及时评估。

这是我的问题:

  1. seq 的调用与对 rnf 的调用是否多余,后者已经将值计算为正常形式?
  2. 如果我更愿意将评估时间设为弱头范式而不是范式,我可以简单地将 rnf 替换为 rwhnf 吗?那么这 3 个调用 evaluaterwhnfseq 是否是多余的,这样我就可以消除对最后 2 个的使用并且只对 evaluate 感到满意?
  1. seq的调用不是多余的,因为rnf x需要求值(对WHNF)才能对x求值为NF,也就是seq的工作。

  2. 是的,我相信是这样:只是

    evaluate $ 1 + y
    return ()
    

但是!如果这里 evaluate 的目的是处理异常,我认为它不起作用,因为它们也可以从 rnf x 中抛出。如果不是,我看不到

的好处
rnf (1 + y) `seq` return ()

forceevaluate 文档都提出了一个更简单的替代方案:

evaluate $ force $ 1 + y
return ()

也许该页面是在 force 可用之前写的?尽管如此,除非我遗漏了什么,

evaluate $ rnf $ 1 + y

会正确完成工作。