Reputation: 5301
I am going through the tutorial at https://en.wikibooks.org/wiki/Haskell/Monad_transformers
I wrote following piece of codes. One without and other with MonadTransformer
instance :
-- Simple Get Password functions.
getPassphrase1 :: IO (Maybe String)
getPassphrase1 = do
password <- getLine
if isValid password
then return $ Just password
else return Nothing
askPassphrase1 :: IO ()
askPassphrase1 = do
putStrLn "Enter password < 8 , alpha, number and punctuation:"
p <- getPassphrase1
case p of
Nothing -> do -- Q1. ### How to implement this with `MonadTrans` ?
putStrLn "Invalid password. Enter again:"
askPassphrase1
Just password ->
putStrLn $ "Your password is " ++ password
-- The validation test could be anything we want it to be.
isValid :: String -> Bool
isValid s = length s >= 8
&& any isAlpha s
&& any isNumber s
&& any isPunctuation s
Another using MonadT
which i wrote myself.
getPassphrase2 :: MaybeT IO String
getPassphrase2 = do
password <- lift getLine
guard $ isValid password
return password
askPassphrase2 :: MaybeT IO ()
askPassphrase2 = do
lift $ putStrLn "Enter password < 8 , alpha, number and punctuation:"
p <- getPassphrase2
-- Q1. How to print "Invalid password" ?
lift $ putStrLn $ "Your password is " ++ p
-- The validation test could be anything we want it to be.
isValid :: String -> Bool
isValid s = length s >= 8
&& any isAlpha s
&& any isNumber s
&& any isPunctuation s
main :: IO ()
main = do
a <- runMaybeT askPassphrase2
return ()
Both works.
But i am unable to understand how to add wrong password
support in MonadTrans
example. ?
Also, is main
method ok.. or it can be written in a better way ?
Upvotes: 0
Views: 189
Reputation: 52059
guard
is the not what you want in the MaybeT approach. To check for an invalid password and be able to provide your own processing in that case you would just use your original version of getPassphase
and lift it into the the MaybeT IO
monad:
getPassphease2 = do result <- lift $ getPassphrase1
case result of
Nothing -> lift $ putStrLn "bad password"
Just pw -> lift $ putStrLn "your password is: " ++ pw
Explanation...
The MaybeT IO
monad is for giving IO-actions the capability to fail and having that failure automatically handled by the monad. If any step fails, control goes all the way back to the runMaybeT
and runMaybeT
returns Nothing
. It's like throwing an exception.
The point of using MaybeT
is that you do no have to explicitly check to see if a step has failed - that checking is performed by the MaybeT
monad after each step is called. That means you can write code assuming that each preceding step has succeeded - i.e. as if you were on the "happy path". This also means that you can't do something different if the previous step failed.
One possibility using the MaybeT
version of getPassphrase
is this:
main = do result <- runMaybeT askPassphrase2
case result of
Just _ -> return ()
Nothing -> putStrLn "Some failure happened... perhaps wrong password?"
The problem is that if runMaybeT
returns Nothing
it could mean that any step in askPassphrase
failed, not just the guard
.
Another way to use your MaybeT version of getPassphrase
is to have askPassphrase
run it with runMaybeT
:
askPassphrase2 = do result <- lift $ runMaybeT getPassphrase2
case result of
Nothing -> lift $ putStrLn "bad password"
Just pw -> lift $ putStrLn $ "Your password is " ++ pw
Here we're using runMaybeT
like a try-catch block.
Upvotes: 1