我已经问了一个类似的问题:例外和单台变压器,但不知何故,我没有正确地表达自己,并得到了另一个问题的答案,而不是我想问的那个问题(至少我是这么理解的)。
我现在又碰到了这个问题,让我再试一次来阐述我的问题.
我必须编写一个函数,其中包含一个可以保存身份验证密钥的服务器,以及一个保存身份验证密钥内容的目标文件。
saveAuthKey :: Text -> Server -> IO (Either Text Text)函数可以在以下三种情况下返回Left:
在我看来,这是EitherT的主要候选人。
所以我首先要说的是:
{-# LANGUAGE OverloadedStrings #-}
import Control.Error
import Control.Monad.Trans
import Data.Text (Text)
import qualified Data.Text as T
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Control.Exception
data Server = Server { authKey :: Maybe ByteString }
main = putStrLn "OK"
saveAuthKey :: Text -> Server -> IO (Either Text Text)
saveAuthKey path server = do
result <- try $ runEitherT $ do
targetFile <- hoistEither $ note "Invalid target file name"
$ T.stripPrefix "file://" path
key <- hoistEither $ note "No authentication key for that server!"
$ authKey server
lift $ BS.writeFile (T.unpack targetFile) key我将try应用于runEitherT之上,因此try将结果封装在另一个Either中。不太优雅。但如果我不直接试一试,例外情况就不会被抓住。在我的前一个问题中,我试图将try放在lift旁边的runEitherT中,这也不能很好地工作。
所以,如果你必须用这个签名写一个这样的函数,你会如何处理呢?我也明白,我应该让一些例外通过而不是系统地捕捉SomeException,我不认为它与我的问题直接相关。假设使用try,我将捕获相关错误(磁盘已满,没有写权限等等)。
我根本无法执行try,并让调用方处理它(毕竟所有这些函数都在IO monad中,因此存在风险),但是在某些时候,必须有人使用try。在我的例子中,我使用的是hsqml库,这是haskell中处理的Javascript调用,如果我允许应用程序中的异常崩溃。
编辑:我给出了这个问题的当前解决方案,在这个承诺中。不过,我觉得可以在这个函数中实现更好的功能,而不必更改应用程序其余部分的设计。请注意,我捕获了所有的异常,我知道这是不建议的,但是暂时可以。没有比这更好的事情了吗?还是我把问题搞错了?
发布于 2015-01-23 00:45:41
也许这就是你想要做的?我将try推入可以抛出的特定调用中,并使用bimapEitherT将异常转换为Text。
saveAuthKey :: ObjRef ProjectViewState -> Text -> ObjRef (Entity Server) -> IO (Either Text Text)
saveAuthKey _ path (entityVal . fromObjRef -> server) = runEitherT $ do
(targetFile, key) <- hoistEither $
(,) <$> note "Invalid target file name"
(T.stripPrefix "file://" path)
<*> note "No authentication key for that server!"
(serverAuthKey server)
bimapEitherT textEx (const mempty) . EitherT . try $
BS.writeFile (T.unpack targetFile) key但是,我觉得这有点过火了,因为可以抛出异常的部分被本地化为一个调用(BS.writeFile),而能够返回Left的部分都是事先发生的纯计算。当您的代码在很大程度上交织了Either和IO逻辑时,Either是很好的,但是在这里,分离是非常清楚的。下面是我如何在没有EitherT的情况下编写它
saveAuthKey :: ObjRef ProjectViewState -> Text -> ObjRef (Entity Server) -> IO (Either Text Text)
saveAuthKey _ path (entityVal . fromObjRef -> server) =
either (return . Left) save authKey
where authKey = (,) <$> note "Invalid target file name"
(T.stripPrefix "file://" path)
<*> note "No authentication key for that server!"
(serverAuthKey server)
save (targetFile, key) = either (Left . textEx) (const (Right ""))
<$> try (BS.writeFile (T.unpack targetFile) key)https://stackoverflow.com/questions/28096091
复制相似问题