在Haskell中强制执行

Sve*_*mir 3 haskell exception-handling strict

在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) [...]
Run Code Online (Sandbox Code Playgroud)

该函数采用Type表示预期异常和IO操作.问题是,大部分时间我都没有抛出异常,即使我本来应该抛弃,因为懒惰.通常,失败的部分fun实际上从未在此进行过评估.

为了解决这个问题,我试图(fun >> assertFailure msg)(seq fun $ assertFailure msg).我还尝试启用BangPatterns扩展并在fun绑定之前发出爆炸声,但没有一个帮助.那么我怎么能真正强迫Haskell fun严格评估呢?

Joa*_*ner 5

你必须区分:

  • 评估类型的值 IO a
  • 运行由它表示的动作,可能有副作用并返回类型的值a,和
  • 评估类型a(或部分)的结果.

这些总是按顺序发生,但不一定全部发生.代码

foo1 :: IO a -> IO ()
foo1 f = do
   seq f (putStrLn "done")
Run Code Online (Sandbox Code Playgroud)

只做第一次,而

foo2 :: IO a -> IO ()
foo2 f = do
   f -- equivalent to _ <- f
   putStrLn "done"
Run Code Online (Sandbox Code Playgroud)

第二次也是最后一次

foo3 :: IO a -> IO ()
foo3 f = do
   x <- f 
   seq x $ putStrLn "done"
Run Code Online (Sandbox Code Playgroud)

也是第三个(但是在seq列表等复杂数据类型上使用的常用注意事项).

尝试这些参数并观察它foo1,foo2foo3以不同的方式对待它们.

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"))}
Run Code Online (Sandbox Code Playgroud)