很难将好问题标题定为新手。请使这个问题搜索友好=)
尝试编写我的第一个“真正的”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。:对其他风格错误的评论当然非常受欢迎!
答案 0 :(得分:5)
如果你已经在使用mtl
,那么bheklilr在他的评论中给出的解决方案是一个很好的解决方案。让parseConfiguration
适用于任何实现MonadError
的monad。
如果由于某种原因你没有使用mtl
,而只使用transformers
,那么你需要一个类似Monad n => Except e a -> ExceptT e n a
类型的功能,&#34;提升&# 34; <{1}} Except
超过一些monad。
我们可以使用ExceptT
来构建这个函数,这个函数可以改变mapExceptT :: (m (Either e a) -> n (Either e' b)) -> ExceptT e m a -> ExceptT e' n b
变换器的基本monad。
ExceptT
真的是Except
,所以我们想要的是打开ExceptT Identity
并返回新monad中的值:
Identity
您也可以这样定义:
hoistExcept :: Monad n => Except e a -> ExceptT e n a
hoistExcept = mapExceptT (return . runIdentity)