Haskell 用EitherT处理异常

Haskell 用EitherT处理异常,haskell,monad-transformers,Haskell,Monad Transformers,我已经在那里问了一个类似的问题:但不知怎么的,我没有正确地表达自己,并且得到了另一个问题的答案,而不是我想问的问题(至少我是这样理解的) 我现在又碰到了这个问题,让我再试着阐述一下我的问题 我必须编写一个函数,使用一个可能持有身份验证密钥的服务器和一个保存身份验证密钥内容的目标文件 saveAuthKey :: Text -> Server -> IO (Either Text Text) 在以下三种情况下,该函数可能返回Left: 目标路径格式不正确:不是以“file://”开头

我已经在那里问了一个类似的问题:但不知怎么的,我没有正确地表达自己,并且得到了另一个问题的答案,而不是我想问的问题(至少我是这样理解的)

我现在又碰到了这个问题,让我再试着阐述一下我的问题

我必须编写一个函数,使用一个可能持有身份验证密钥的服务器和一个保存身份验证密钥内容的目标文件

saveAuthKey :: Text -> Server -> IO (Either Text Text)
在以下三种情况下,该函数可能返回
Left

  • 目标路径格式不正确:不是以“file://”开头
  • 服务器未持有身份验证密钥
  • 保存文件的密钥时发生IO错误
  • 在我看来,这是
    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
    
    {-#语言重载字符串}
    导入控制。错误
    进口管制.Monad.Trans
    导入数据。文本(Text)
    导入符合条件的数据。文本为T
    导入Data.ByteString(ByteString)
    将符合条件的数据.ByteString作为BS导入
    导入控制。异常
    数据服务器=服务器{authKey::可能是ByteString}
    main=putStrLn“正常”
    saveAuthKey::Text->Server->IO(文本或文本)
    saveAuthKey路径服务器=do
    
    结果也许这就是你想要做的?我将
    try
    推入可以抛出的特定调用,并利用
    bimapEitherT
    将异常转换为
    文本

    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
    

    我不明白,如果要立即运行monad转换器,为什么要使用monad转换器。也许您想要
    saveAuthKey::Text->Server->EitherT您的例外类型IO Text
    ?然后,当您作为
    IO(YourExceptionType Text)
    运行
    saveAuthKey
    时,它将捕获类型为
    YourExceptionType
    的异常,并传播其他所有内容。然而,
    Text
    肯定不是一个好的异常类型……这直接转到javascript,我将字符串返回到JS。我不能返回/抛出真正的异常。但是让我们忽略这一点。我的问题是捕获
    IO
    调用可能抛出的某些异常类型,并使其与
    EitherT
    匹配。您会问为什么要使用变压器立即运行它。我想要
    单子效果。。。我也不能使用
    ,因为涉及到IO,所以我移动到
    EitherT
    ,然后执行它。对我来说似乎是合乎逻辑的,但可能(很可能)我做错了。此外,如果该函数会触发FileExists异常(例如),我认为它应该将其包装在它返回的
    中,而不是让它抛出异常。调用方可以使用其
    案例从中恢复。如果发生另一个意外的异常(例如OutOfMemory),那么它应该允许它传播。对吧?我不确定javascript和它有什么关系。如果您的IO操作引发异常,您可以捕获它,无论您是在
    EitherT e IO a
    中还是在
    IO a
    中。另外,我也不知道捕捉哪些特定的异常会有什么不同——您总是可以捕捉某些特定的异常并传播所有其他异常。如果您用一个例子说明当前函数的哪些行为是不正确的,这会有所帮助。因此,我想捕捉一些异常。这就是我最终想要的。问题中所述的当前函数不可编译。使用
    try$runEitherT
    我在
    other
    中得到一个
    other
    ,但我想要一个
    other
    。它很容易“变平”,但感觉很难看。如果我在
    EitherT
    中捕捉到异常,那么我也不知道如何以令人满意的方式完成(这是我的另一个问题)。如果你能完成这个函数的两个实现,捕获一些IO异常,让其他异常以惯用的方式通过,一个捕获IO,一个捕获EIT,这就是我想要的。非常感谢!对于这两个代码示例。我也考虑过拆分非IO操作并使用元组。我也错过了bimapEitherT。但是我想要的答案是
    EitherT。多多少少试试。我一直试图在那里放置一个
    电梯
    ,这显然是不需要的(需要花一些时间来详细了解monad transformers是如何工作的)。对变压器所需的样板/管道有点失望,但谢天谢地,你并不经常需要这些。
    
    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)