绝对强制在Haskell中捕获错误

Sav*_*nel 3 haskell exception

我正在使用本身使用zip-archive的hs-excelx库.zip-archive正在达到它调用的条件fail,在该特定上下文中,它会计算为对其的调用error.这是对error纯代码的调用.

我正在尝试检测特定文件是否实际上是Excel文件.实际上我必须在不崩溃的情况下检测到这一点,所以我编写了一个名为isExcel的函数来进行检测:

import qualified Data.Excelx as E

isExcel :: BS.ByteString -> Bool
isExcel = maybe False (\_ -> True) . E.toExcelx
Run Code Online (Sandbox Code Playgroud)

现在,问题在于这只是一种形式.如果你在一个不是zip存档的字节串上调用E.toExcelx,那么zip-archive就会error出来.

但是,我知道我正在调用isExcelIO代码,所以我编写了一个IO函数来尝试捕获这样的错误:

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

到底是怎么回事?根据我在http://www.haskell.org/haskellwiki/Error_vs._Exception和其他地方所读到的内容,我应该捕获异常并将其转换为有用的东西. ie在我的代码可能是一个形实转换为BOOL,却怎么也运行seq在ie可能留下什么不计算?当我甚至无法弄清楚如何强制评估值时,我怎么可能捕获这样的异常?我没有时间进入zip-archive中进行正确的错误处理.

Joh*_*n L 7

这里几乎没有问题.首先,要强制评估IO中的值,请使用Control.Exception.evaluate.这个函数有类型evaluate :: a -> IO a,它基本上是一个钩子,告诉编译器强制进行评估.它存在的唯一原因是启用异常处理.

接下来,您需要一些机制来实际捕获异常.您目前正在使用handle,但是Control.Exception.try,具有该类型try :: Exception e => IO a -> IO (Either 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)
Run Code Online (Sandbox Code Playgroud)

当然你知道isExcel只会回来True.您可以toExcel直接使用,并修改该either行以适应该行.

至于问题的根源,

a `seq` a
Run Code Online (Sandbox Code Playgroud)

是完全等同于a.这意味着,"在您评估时(第二次)a,评估(第一次)a".换句话说,它仍然太懒惰了.这就是你需要的原因evaluate.