簡體   English   中英

對Haskell的惰性表達式中評估的未定義進行單元測試

[英]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 (代表“標准格式數據”)約束,因此您可能會發現自己為數據結構派生了GenericNFData


1感謝@AlexisKing指出, evaluate 確實會將其參數推入WNHF,這就是為什么head $ map (+1) [undefined, 2, 3]確實會觸發錯誤的原因。 take的情況下,這還不夠。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM