我正在使用 hs-excelx 库,它本身使用 zip-archive。 zip-archive 达到了调用 fail
的条件,在该特定上下文中,该条件的计算结果为对 error
的调用。这是纯代码中对 error
的调用。
我正在尝试检测特定文件是否实际上是 Excel 文件。实际上,我有必要在不崩溃的情况下检测到这一点,因此我编写了一个名为 isExcel 的函数来进行检测:
import qualified Data.Excelx as E
isExcel :: BS.ByteString -> Bool
isExcel = maybe False (\_ -> True) . E.toExcelx
现在,问题是这只是一种形式。如果您对不是 zip 存档的字节字符串调用 E.toExcelx,zip-archive 将简单地错误
输出。
但是,我知道我在 IO 代码中调用 isExcel
,因此我编写了一个 IO 函数来 try catch 如下错误:
import qualified Data.ByteString.Lazy as BS
import Control.Exception
sd :: BS.ByteString -> IO Bool
sd bs = handle handler $ do
ie <- return $ Excel.isExcel bs
return (ie `seq` ie)
where
handler :: SomeException -> IO Bool
handler e = return False
> sd BS.empty
*** Exception: too few bytes. Failed reading at byte position 4
这是怎么回事?根据我在http://www.haskell.org/haskellwiki/Error_vs._Exception读到的内容和其他地方,我应该捕获异常并将其转换为有用的东西。我的代码中的 ie 可能是对 Bool 的 thunk,但是在 ie 上运行 seq 怎么可能留下任何未计算的内容?当我什至不知道如何强制评估一个值时,我怎么可能捕获这样的异常?我没有时间进入 zip 存档并进行适当的错误处理。
最佳答案
这里几乎没有什么问题。首先,要强制评估 IO 中的值,请使用 Control.Exception.evaluate。该函数的类型为 evaluate::a -> IO a,它基本上是一个告诉编译器强制求值的钩子(Hook)。它存在的唯一原因是启用异常处理。
接下来,您需要一些机制来实际捕获异常。您当前使用的是 handle
,但是 Control.Exception.try
,其类型为 try::Exception e => IO a -> IO(要么 e a )
,这里使用起来可能更简单一些。这种类型有点奇怪,但这意味着,对于您指定的某些异常类型,它 try
将返回值或异常。如果计算抛出的异常不是您指定的类型(并且不能强制为该类型),try
将重新抛出该异常。
对 error
的调用会产生 ErrorCall
类型的异常,因此要处理您可以使用的异常
sd :: BS.ByteString -> IO Bool
sd bs = do
ie <- try $ evaluate $ Excel.isExcel bs
either (const False) (id) (ie :: Either ErrorCall Bool)
当然你知道isExcel
只会返回True
。您可以直接使用toExcel
,并修改任一
行以适应这一点。
至于你的问题的根源,
a `seq` a
与a
完全等价。它的意思是,“当你评估(第二个)a
时,也评估(第一个)a
”。换句话说,还是太懒了。这就是为什么您需要评估
。
关于exception - 绝对强制捕获 Haskell 中的错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21105137/