在 Haskell 中做一些 TTD 时,我最近开发了以下功能:
import Test.HUnit
import Data.Typeable
import Control.Exception
assertException :: (Show a) => TypeRep -> IO a -> Assertion
assertException errType fun = catch (fun >> assertFailure msg) handle
where
msg = show errType ++ " exception was not raised!"
handle (SomeException e) [...]
该函数采用预期异常和 IO 操作的类型表示。问题是,大多数时候,由于懒惰,我没有抛出异常,尽管我应该抛出异常。通常,fun
的失败部分实际上从未在这里进行评估。
为了解决这个问题,我尝试将 (fun >> assertFailure msg)
替换为 (seq fun $assertFailure msg)
。我还尝试启用 BangPatterns 扩展并在 fun
绑定(bind)之前添加一个 bang,但没有任何帮助。那么我怎样才能真正迫使 Haskell 严格评估 fun
呢?
最佳答案
你必须区分:
- 计算
IO a
类型的值 - 运行它所代表的操作,这可能会产生副作用并返回
a
类型的值,并且 - 评估类型
a
(或其部分)的结果。
这些总是按这个顺序发生,但不一定是全部。代码
foo1 :: IO a -> IO ()
foo1 f = do
seq f (putStrLn "done")
只会做第一个,而
foo2 :: IO a -> IO ()
foo2 f = do
f -- equivalent to _ <- f
putStrLn "done"
也执行第二个和最后一个
foo3 :: IO a -> IO ()
foo3 f = do
x <- f
seq x $ putStrLn "done"
也执行第三个(但在复杂数据类型(如列表)上使用seq
的常见注意事项适用)。
尝试这些参数并观察 foo1
、foo2
和 foo3
对它们的处理方式不同。
f1 = error "I am not a value"
f2 = fix id -- neither am I
f3 = do {putStrLn "Something is printed"; return 42}
f4 = do {putStrLn "Something is printed"; return (error "x has been evaluated")}
f5 = do {putStrLn "Something is printed"; return (Just (error "x has been deeply evaluated"))}
关于haskell - 在 Haskell 中加强严格性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26445240/