我正在使用本身使用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
现在,问题在于这只是一种形式.如果你在一个不是zip存档的字节串上调用E.toExcelx,那么zip-archive就会error
出来.
但是,我知道我正在调用isExcel
IO代码,所以我编写了一个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
到底是怎么回事?根据我在http://www.haskell.org/haskellwiki/Error_vs._Exception和其他地方所读到的内容,我应该捕获异常并将其转换为有用的东西. ie
在我的代码可能是一个形实转换为BOOL,却怎么也运行seq
在ie
可能留下什么不计算?当我甚至无法弄清楚如何强制评估值时,我怎么可能捕获这样的异常?我没有时间进入zip-archive中进行正确的错误处理.
这里几乎没有问题.首先,要强制评估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)
当然你知道isExcel
只会回来True
.您可以toExcel
直接使用,并修改该either
行以适应该行.
至于问题的根源,
a `seq` a
是完全等同于a
.这意味着,"在您评估时(第二次)a
,评估(第一次)a
".换句话说,它仍然太懒惰了.这就是你需要的原因evaluate
.