但我想知道:如果节点一路上断开链接,比如在最后一个“do”块之前,会发生什么?
你会得到一个例外,在这种情况下这没什么大不了的。您正在执行 IO 并且可以处理它。我认为您不会找到一个万无一失的平台无关解决方案。
您制作一个有用的包装器 monad 来保持代码可读怎么样?它可能比您喜欢的更多样板(在这种情况下,只需将整个函数调用包装起来catch
)但对于较大部分的代码可能会非常好:
data MyErr = CaughtException SomeException
-- ^ You might want to just catch
-- specific types of exceptions here
| MyErr String
type M = ExceptT MyErr IO
myErr :: String -> M a
myErr = throwE . MyErr
myIO :: IO a -> M a
myIO io = ExceptT (catch (Right <$> io) (pure . Left . CaughtException))
The M
monad 允许您捕获所有丑陋的异常以及您的程序逻辑并将其捆绑到一个单一的Either
结果。你可以这样使用它:
browseSimple :: FilePath -> IO (Either MyErr [FilePath])
browseSimple x = runExceptT $ do
isAvailable <- myIO $ doesPathExist x
when (not isAvailable) (myErr $ "File not found: " ++ x)
isFile <- myIO $ doesFileExist x
when isFile (myErr x)
isDirectory <- myIO $ doesDirectoryExist x
when (not isDirectory) (myErr $ "Unknown filesystem node: " ++ x)
listing <- myIO $ listDirectory x
return ((x </>) <$> listing)
你可以增强一些东西,比如提供myIO
有了更多的信息,所以如果/当事情失败时,你可以将结果与你的操作中事情急剧下降的地方联系起来,但这通常是矫枉过正的。
我们可以使用whenM进一步清理事情(类型未测试):
whenM io m = myIO io >>= \b -> when b m
browseSimple2 :: FilePath -> IO (Either MyErr [FilePath])
browseSimple2 x = runExceptT $ do
whenM (not <$> doesPathExist x)
(myErr $ "File not found: " ++ x)
whenM (doesFileExist x)
(myErr x)
whenM (not <$> doesDirectoryExist x)
(myErr $ "Unknown filesystem node: " ++ x)
myIO $ (x </>) <$> listDirectory x