haskell - 我可以把 IO monad 漂亮地放在这个纯函数上吗?

标签 haskell monad-transformers

作为新手,制定好的问题标题是相当困难的。请使此问题易于搜索 =)

尝试编写我的第一个“真正的”Haskell 程序(即不仅仅是 Project Euler 的东西),我尝试使用漂亮的错误消息读取和解析我的配置文件。到目前为止,我有这个:

import Prelude hiding (readFile)
import System.FilePath (FilePath)
import System.Directory (doesFileExist)
import Data.Aeson
import Control.Monad.Except
import Data.ByteString.Lazy (ByteString, readFile)

-- Type definitions without real educational value here

loadConfiguration :: FilePath ->  ExceptT String IO Configuration
loadConfiguration path = do
    fileContent     <- readConfigurationFile "C:\\Temp\\config.json"
    configuration   <- parseConfiguration fileContent
    return configuration

readConfigurationFile :: FilePath -> ExceptT String IO ByteString
readConfigurationFile path = do
    fileExists <- liftIO $ doesFileExist path
    if fileExists then do
        fileContent <- liftIO $ readFile path        
        return fileContent
    else
        throwError $ "Configuration file not found at " ++ path ++ "."

parseConfiguration :: ByteString -> ExceptT String IO Configuration
parseConfiguration raw = do
    let result = eitherDecode raw :: Either String Configuration
    case result of 
        Left message        -> throwError $ "Error parsing configuration file: " ++ message
        Right configuration -> return configuration

这可行,但是 parseConfiguration 中的 IO monad 不是必需的,应该消失。但当然,我不能直接放弃它,而且我还没有找到一种方法将 parseConfiguration 更改为纯粹的东西,同时保持 loadConfiguration 的美观。

正确的写法是什么?如果文档中回答了这个问题,很抱歉,但我没有找到它。我认为阅读 hackage 文档是一项与我的其他 Haskell 技能一样缓慢增长的技能。 =)

P.S.:当然,非常欢迎对其他风格错误提出评论!

最佳答案

如果您已经在使用 mtl,那么 bheklilr 在他的评论中给出的解决方案是一个很好的解决方案。让 parseConfiguration 在任何实现 MonadError 的 monad 上工作。

如果出于某种原因您不使用 mtl,而仅使用 transformers,那么您需要一个类型类似于 Monad n => except 的函数e a -> exceptT e n a 在某些 monad 上将 Except“提升”到 ExceptT 中。

我们可以使用 mapExceptT::(m (Either e a) -> n (Either e' b)) -> exceptT e m a -> exceptT e' n b 构建这个函数,该函数可以更改 ExceptT 转换器的基本 monad。

Except 实际上是 ExceptT Identity,所以我们想要的是解开 Identity 并返回新 monad 中的值:

hoistExcept :: Monad n => Except e a -> ExceptT e n a
hoistExcept = mapExceptT (return . runIdentity)

你也可以这样定义它:

hoistExcept :: Monad n => Except e a -> ExceptT e n a
hoistExcept = ExceptT . return . runIdentity . runExceptT

关于haskell - 我可以把 IO monad 漂亮地放在这个纯函数上吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26802456/

相关文章:

c - haskell FFI : Calling FunPtrs

Haskell Riak 驱动程序 : Making a simple 'put' operation

haskell - 我怎样才能编写一个也可以进行错误处理的状态单子(monad)?

haskell - 在 Writer monad 中传递和聆听的意义何在?

scala - 不同的单子(monad)用于理解

haskell - 理解 concatMap 递归

haskell - ImpredicativeTypes 的简单示例

haskell - 如何很好地评估嵌套的 StateT 和 ErrorT monad?

haskell - 使用 Reader 扩展 ServerPartT Monad

oop - 相当于在 Haskell 中创建对象