For my previous question on chaining failures, Michael Snoyman had suggested I use MaybeT to run them so if any of them fails, it will just short-circuit to Nothing.
I was under the impression runDb runs everything in a transaction. So shouldn't a failure at any point in code automatically rollback the transaction?
mauth <- runDb $ runMaybeT $ do
valid <- MaybeT $ return $ listToMaybe errs
uid <- MaybeT $ insertUnique u
vid <- MaybeT $ getBy $ UniqueField v -- this step fails but previous insert does not roll back
auth <- liftIO $ createAuthToken uid
return auth
When I run the above code, the getBy fails but user was still inserted. Am I misunderstanding that runDb will rollback on a Nothing inside MaybeT? Do I need to use some other Monad for this to work?
Appreciate your thoughts on how to best rollback on failure.
Update:
This is what I ended up doing per Michael's suggestion.
mauth <- runDb $ do
ma <- runMaybeT $ do
valid <- ...
case ma of
Just _ -> return ma
Nothing -> liftIO $ throwIO MyException
Now I need to figure out how to catch this exception nicely outside and return a proper error message back.
Thanks!
Returning Nothing is not the same thing as a failure. You'd need to throw a runtime exception (via something like throwIO) for Persistent to treat it as a rollback situation.
Related
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 ?
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.
First of all I'm a Haskell noob so if there is something obvious I'm doing wrong sorry.
Say I have something like:
Object1
stuff Text
other Object2Id Maybe
Object2
otherStuff Text
in my config/models file
How do I get the Object2 associated to Object1 if I have a handler of type:
getObject1R :: Object1Id -> Handler Html
If I understand correctly I want to go from a Maybe Object2Id to a Maybe Object with calling runDB $ get in the middle and with everything I've tried the types don't line up properly.
Thanks
EDIT
here is one attempt that I feel is close but isn't correct:
object1 <- runDB $ get object1Id
maybeObj2 <- case (object1Other object1) of
Just obj2Id -> Just $ runDB $ get obj2Id
Nothing -> Nothing
EDIT 2 (figured it out)
So I figured out how to get it to compile and work! Thanks for the comments and help.
here is my code (in the handler):
(object1, maybeObject2) <- runDB $ do
object1 <- get404 object1Id
maybeObject2 <- case (object1Other object1) of
Just object2Id -> get object2Id
Nothing -> return Nothing
return (object1,maybeObject2)
I'm sure there are better/faster/easier ways to do it, but this worked.
So I figured out how to get it to compile and work! Thanks for the comments and help.
here is my code (in the handler):
(object1, maybeObject2) <- runDB $ do
object1 <- get404 object1Id
maybeObject2 <- case (object1Other object1) of
Just object2Id -> get object2Id
Nothing -> return Nothing
return (object1,maybeObject2)
I'm sure there are better/faster/easier ways to do it, but this worked.
I am trying to figure out if there is a way to avoid lots of case statements while inserting records into the DB.
My current code sort of looks like this:
mt1 <- runDb $ do
muid <- insertUnique user
case muid of
Just uid -> do
let t1 = Table1 {..., user = uid}
maid <- insertUnique t1
case maid of
Just aid -> do
mo <- getBy $ UniqueField "somevalue"
case mo of
Just (Entity oid o) -> do
mcid <- insertUnique Table2 {..., oid = oid}
case mcid of
Just cid -> do
mfid <- insertUnique Table3 {..., cid = cid}
case mfid of
Just fid -> Just t1 -- original t1 that was created at the top of the chain
Nothing -> Nothing
Nothing -> Nothing
Nothing -> Nothing
Nothing -> Nothing
Nothing -> Nothing
Nothing -> Nothing
First of all, I have issues getting the code to compile but instead of trying to debug that, I wanted to see if there is a better way to do this.
At a conceptual level, I want to do something like below, where all the Maybe values get unwrapped automatically to be used in subsequent invocations. If any point, we hit a Nothing, I just want to return Nothing. The whole code will run in a single transaction so if we hit a Nothing in between, the transaction is rolled back
runDb $ do
uid <- insertUnique user
let t1 = Table1 {..., user = uid} -- uid is unwrapped to just the value
aid <- insertUnique t1
o <- getBy $ UniqueField "somevalue"
cid <- insertUnique Table2 {..., oid = oid}
fid <- insertUnique Table3 {..., cid = cid}
Just t1
I am Haskell beginner so I only have superficial understanding of Monads (I can use the simple ones fine) but when it comes to use it inside something like runDb of Persistent, I have no idea how to put the pieces together.
Any suggestions on how I can simply the logic so I don't end up checking for failure each step of the way?
Update: Based on Michael's answer, I did something like this and it automatically unwraps the maybes when used.
mt1 <- runDb $ runMaybeT $ do
uid <- MaybeT $ insertUnique user
...
case mt1 of
Just t -> return t
Nothing -> lift $ left ...
Thanks!
The standard approach to something like this is the MaybeT monad transformer. Something like the following will probably work:
runMaybeT $ do
uid <- MaybeT $ insertUnique user
...
I'm trying to access mongo using the mongodb haskell drivers (the snap driver appears to be broken for snap > 0.5) in splice.
This is as far as I've got so far:
testSplice :: Splice AppHandler
testSplice = do
record <- liftIO $ do
pipe <- runIOE $ connect (host "127.0.0.1")
results <- access pipe master "db" (find $ select [] "coll")
close pipe
rest result
return $ [TextNode $ T.pack $ show $ records]
I understand that I need to use liftIO there, as the mongo action occurs inside an IO monad, and I want to pull that back out. Where my understanding breaks down is the result of compiling that splice:
Couldn't match expected type `IO a0'
with actual type `Action m0 [Database.MongoDB.Document]'
I'm sorry to post a "Send me the codes plz" question, but I'm at loss: where am I going wrong, and how do I make this work?
Here is your function annotated with type signatures. I think this makes it
pretty clear where the problem lies.
testSplice :: Splice AppHandler
testSplice = do
record <- liftIO $ do
pipe <- runIOE $ connect (host "127.0.0.1") -- :: IO Pipe
results <- access pipe master "db" (find $ select [] "coll")
-- ^ :: IO (Either Failure Cursor)
close pipe -- :: IO ()
rest result -- :: Action m [Document]
return $ [TextNode $ T.pack $ show $ records]
Everything inside the "liftIO $ do" block must be an IO action. The last line
"rest result" is not. One solution is to prepend that line with 'access pipe
master "db"' just like you've done with find. Another solution is to avoid
calling "access pipe..." twice and replace the find line with the following:
result <- access pipe master "db" (find (select [] "coll") >>= rest)
Then replace the "rest result" line with "return result"
What Daniel says about the find line not needing liftIO is correct, but in
this case it doesn't matter because IO has a MonadIO instance. So it's probably just as easy to keep all the liftIO stuff in one block.
I am not a MongoDB expert, so I'm not 100% sure (and I can't test it), but I suspect that you've got your liftIO in the wrong place. We have liftIO :: MonadIO m => IO a -> m a, so we should apply liftIO to actions that are actually IO, but which we want to be something bigger than IO. I suspect that access is a function with a bigger-than-IO return type. Assuming runIOE, close, and rest all actually have IO return types, we'd then do something like this:
testSplice = do
pipe <- liftIO . runIOE $ connect (host "127.0.0.1")
results <- access pipe master "db" (find $ select [] "coll") -- note: no liftIO on this one because it's presumably already lifted
liftIO $ close pipe
record <- liftIO $ rest result
return [TextNode . T.pack . show $ records]
If some of those actions actually are not IO things, then you can drop the liftIO from those ones.
As you observed, this can be cleaned up a bit: any adjacent lines that start with liftIO can be coalesced. So if the above turns out to be the right places for liftIOs, then it could also be written as:
testSplice = do
pipe <- liftIO . runIOE $ connect (host "127.0.0.1")
results <- access pipe master "db" (find $ select [] "coll")
liftIO $ do
close pipe
record <- rest result
return [TextNode . T.pack . show $ records]
(The last one there is OK because return = liftIO . return for any sane implementation of liftIO.)
I am using the Haskeline package and I want to get three strings in a row from the command line before I do anything and I have come up with what seems to be a neat solution to me. But I am sure that there might be a better way to do it. I am looking for best practices while using the Haskeline package. Please evaluate the merits of the following example code:
import System.Console.Haskeline
import Control.Monad.Trans
import Control.Monad.Maybe
import Data.Maybe
import Control.Monad
main :: IO ()
main = runInputT defaultSettings (runMaybeT getStrings) >>= print
getStrings :: MaybeT (InputT IO) (String, String, String)
getStrings = do
mone <- lift $ getInputLine "food> "
notNothing mone
mtwo <- lift $ getInputLine "drink> "
notNothing mtwo
mthree <- lift $ getInputLine "dessert> "
notNothing mthree
return (fromJust mone, fromJust mtwo, fromJust mthree)
where
notNothing a = guard (a /= Nothing)
As you can see it accomplishes the task of early termination but it looks a bit yucky still. I'm thinking of trying to convert the notNothing's and the getInputLine's into a single line like:
mone <- notNothing =<< lift $ getInputLine "food> " -- does not type check
Which I think does not look that bad. I think that is pretty clear and concise (though it does not type check so I will have to write a version that does).
However, this is the best I have come up with and my question finally is: How would you go about improving this code to be neater and more readily readable? Am I even on the right track?
Edit: If your guard is something other than 'a /= Nothing' then a nice helper function that I just discovered is:
myGuard s = guard (someConditionFunc s) >> s
Because then you can write (as luqui suggested):
mone <- myGuard =<< (lift $ getInputLine prompt)
Which is pretty cool. But if you are matching against only Nothing then TomMD's answer is better.
Why not just leverage the fact that fail _ = Nothing for the Maybe monad?
mthree <- lift $ getInputLine "dessert> "
notNothing mthree
becomes
Just mthree <- lift $ getInputLine "dessert> "
How about a helper function?
inputLine :: String -> MaybeT (InputT IO) String
inputLine prompt = do
m <- lift $ getInputLine prompt
case m of
Just x -> return x
Nothing -> mzero
This can be shortened considerably using various tricks, but I wanted to be clear. Now you can just forget that getInputLine can fail, MaybeT takes care of that for you.