[英]Unit-testing the undefined evaluated in lazy expression in Haskell
在Haskell中編寫單元測試,其中遇到undefined
時表達式將失敗,這有點棘手。 我用HSpec嘗試了以下方法:
module Main where
import Test.Hspec
import Control.Exception (evaluate)
main :: IO ()
main = hspec $ do
describe "Test" $ do
it "test case" $ do
evaluate (take 1 $ map (+1) [undefined, 2, 3]) `shouldThrow` anyException
無濟於事。 它報告我did not get expected exception: SomeException
如果我在REPL中計算相同的表達式,則會得到:
[*** Exception: Prelude.undefined
CallStack (from HasCallStack):
error, called at libraries\base\GHC\Err.hs:79:14 in base:GHC.Err
undefined, called at <interactive>:2:20 in interactive:Ghci1
問題在於, evaluate
不會將您的表達式強制為NH 甚至WHNF 1 。 在GHCi中嘗試x <- evaluate (take 1 $ map (+1) [undefined, 2, 3])
-它不會給您任何錯誤! 當您粘貼evaluate (take 1 $ map (+1) [undefined, 2, 3])
,這樣做的唯一原因是GHCi還會嘗試打印得到的結果,為此,它最終嘗試評估表達式。
如果要查看已評估了多少thunk,則可以始終在GHCi中使用:sprint
:
ghci> x <- evaluate (take 1 $ map (+1) [undefined, 2, 3])
ghci> :sprint x
x = [_]
如您所見, evaluate
並沒有強迫表達式足夠遠,以至於x
包含一個undefined
。 一個快速的解決方法是使用force
將您要檢查的事物評估為正常形式。
import Test.Hspec
import Control.Exception (evaluate)
import Control.DeepSeq (force)
main :: IO ()
main = hspec $ do
describe "Test" $ do
it "test case" $ do
evaluate (force (take 1 $ map (+1) [undefined, 2, 3] :: [Int]))
`shouldThrow` anyException
force
可以觸發對thunk的求值,直到對參數進行完全求值為止。 請注意,它具有NFData
(代表“標准格式數據”)約束,因此您可能會發現自己為數據結構派生了Generic
和NFData
。
1感謝@AlexisKing指出, evaluate
確實會將其參數推入WNHF,這就是為什么head $ map (+1) [undefined, 2, 3]
確實會觸發錯誤的原因。 在take
的情況下,這還不夠。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.