我有一项任务是实现一个重复询问用户密码的函数,并说明它是否正确。如果密码正常,则显示“存储在数据库中并退出”。我需要使用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
我在这里缺少什么? 谢谢你的帮助!
答案 0 :(得分:1)
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..."