I started using Yesod to develop a small project, this is the first time I use Haskell to create something real. This code that processes the registration form works fine:
postRegisterR :: Handler () postRegisterR = do email <- runInputPost $ ireq textField "email" user <- runInputPost $ ireq textField "user" pwd <- runInputPost $ ireq textField "pwd" cpwd <- runInputPost $ ireq textField "cpwd" if pwd == cpwd && isValidEmail email then do tryInsert email user pwd setSession "user" user redirectUltDest SessionR else do redirect HomeR tryInsert :: Text -> Text -> Text -> Handler () tryInsert email user pwd = do pwdbs <- liftIO $ hashedPwd pwd _ <- runDB $ insert $ User email user pwdbs return ()
Now the problem is that if I log in to the account twice with the same credentials, I get an InternalServerError . This is correct, because in my model configuration there is UniqueUser email username . So I would like to somehow catch and handle this error. How can I do this and, in general, how does Haskell exception handling work when you are dealing with non-IO monads defined in an external library or framework?
PS: I read , but it is useful if you are developing a new library. I tried using the catch function, but I got a lot of type errors.
Edit
Thanks Ankur, your code worked with a small modification to remove this error:
Ambiguous type variable `e0' in the constraint: (Exception e0) arising from a use of `catch' Probable fix: add a type signature that fixes these type variable(s)
code:
tryInsert :: Text -> Text -> ByteString -> Handler Bool tryInsert email user pwd = HandlerT (\d -> catch (unHandlerT (runDB $ insert $ User email user pwd) d >> return True) (\(e :: SomeException) -> return False))
With ScopedTypeVariables Extension ScopedTypeVariables
Edit 2
The final version, after a hint of bennofs:
{-# LANGUAGE ScopedTypeVariables #-} import Control.Exception.Lifted (catch) import Control.Monad (void) postRegisterR :: Handler () postRegisterR = do email <- runInputPost $ ireq textField "email" user <- runInputPost $ ireq textField "user" pwd <- runInputPost $ ireq textField "pwd" cpwd <- runInputPost $ ireq textField "cpwd" if pwd == cpwd && isValidEmail email then do pwdbs <- liftIO $ hashedPwd pwd success <- tryInsert email user pwdbs case success of True -> do setSession "user" user redirectUltDest SessionR False -> redirect HomeR else do redirect HomeR tryInsert :: Text -> Text -> ByteString -> Handler Bool tryInsert email user pwd = do void $ runDB $ insert $ User email user pwd return True `catch` (\(e :: SomeException) -> do return False)
source share