haskell - 在Haskell中使用ErrorT

标签 haskell error-handling monad-transformers

我有一个任务来实现一个功能,该功能反复要求用户输入密码并说出密码是否正确。如果密码确定,则显示“存储在数据库中并退出”。我需要使用ErrorT monad转换器来告诉用户,他的密码不正确,因此基本行为如下所示

GHCi>runErrorT askPassword'
Enter your new password:
qwerty
Incorrect input: password is too short!
qwertyuiop
Incorrect input: password must contain some digits!
qwertyuiop123
Incorrect input: password must contain some punctuations!
qwertyuiop123!!!
Storing in database...
GHCi>
askPassword'就像
data PwdError = PwdError String
type PwdErrorMonad = ErrorT PwdError IO

instance Error PwdError where
    noMsg = PwdError "Unknown error"
    strMsg s = PwdError s 

instance Show PwdError where
    show (PwdError s) = show s 

askPassword' :: PwdErrorMonad ()
askPassword' = do
    liftIO $ putStrLn "Enter your new password:"
    value <- msum $ repeat getValidPassword'
    liftIO $ putStrLn "Storing in database..." 

getValidPassword看起来像这样:
getValidPassword' :: PwdErrorMonad String
getValidPassword' = do
    s <- liftIO getLine
    if length s < 8
        then throwError $ PwdError "Incorrect input: password is too short!"
        else if (not $ any isNumber s)
            then throwError $ PwdError "Incorrect input: password must contain some digits!"
            else if (not $ any isPunctuation s)
                then throwError $ PwdError "Incorrect input: password must contain some punctuations!"
                else return s

我在这里想念什么?
感谢帮助!

最佳答案

ErrorT数据类型在Left字段中记录错误,该字段仅包含一个错误。它无法保存每个错误。如果您只想“引发错误”,请使用IO monad并使用throw/catch中的Control.Exception

如果要使用当前代码,请尝试以下操作:

withErr :: MonadError e m => (e -> m ()) -> m a -> m a
withErr f ac = catchError ac (\e -> f e >> throwError e)

此函数在错误被重新抛出之前对其应用了附加功能。在您的情况下,您只想打印错误:
askPassword' :: PwdErrorMonad ()
askPassword' = do
    liftIO $ putStrLn "Enter your new password:" 
    _ <- msum $ repeat $ withErr (liftIO . print) getValidPassword'
    liftIO $ putStrLn "Storing in database..." 

另一种方法是使用mtl API创建一个函数,该函数运行一个函数,直到它不返回错误为止。
try :: MonadError e m => (e -> m ()) -> m a -> m a
try h ac = go where go = catchError ac (\e -> h e >> go)

您可以对错误采取任何措施,例如,将其收集在一个列表中:
recordErrors :: MonadError e m => m a -> m (a, [e])
recordErrors = runWriterT . try (tell . (:[])) . lift 

但是,您只需要打印它们:
askPassword' :: PwdErrorMonad ()
askPassword' = do
    liftIO $ putStrLn "Enter your new password:"
    _ <- try (liftIO . print) getValidPassword'  
    liftIO $ putStrLn "Storing in database..." 

关于haskell - 在Haskell中使用ErrorT,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30131295/

相关文章:

Haskell:将表达式转换为指令列表

oracle - 即使 PL/SQL 过程引发错误,仍可以写入 OUT 参数值吗?

haskell - 如何使用Monad变形金刚来组合不同的(纯净的和不纯净的)monad?

haskell - 为什么我不能将两个阅读器堆叠在一起?

haskell - 翻转修复/修复

haskell - "Illegal view pattern: fromPathPiece -> Just dyn_abDD"在路由上使用参数时

list - 如何在 Haskell 中提取所有唯一的列表对?

scala - 可以将长度与标题记录长度不同的记录放入bad_record目录

api - 使用远程API时处理错误

haskell - 我怎样才能继续实现这个monad转换器?