Haskell:非IO monad中的异常处理

Haskell:非IO monad中的异常处理,haskell,exception-handling,monads,yesod,Haskell,Exception Handling,Monads,Yesod,我开始使用YesSOD开发一个小项目,这是我第一次使用Haskell做一些真实的事情。 处理注册表的代码可以正常工作: postRegisterR :: Handler () postRegisterR = do email <- runInputPost $ ireq textField "email" user <- runInputPost $ ireq textField "user" pwd

我开始使用YesSOD开发一个小项目,这是我第一次使用Haskell做一些真实的事情。 处理注册表的代码可以正常工作:

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 ()
代码:

启用了
ScopedTypeVariables
扩展

编辑2

最终版本,在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)
{-#语言范围的TypeVariables}
导入控制.Exception.Lifted(catch)
进口管制.单子(无效)
postRegisterR::处理程序()
postRegisterR=do电子邮件
不返回False)

您可以尝试如下所示的方法,基本上
处理程序是
HandlerT
,它是monad transformer(我没有检查下面的代码:))

tryInsert::Text->Text->Text->Handler Bool
tryInsert电子邮件用户pwd=HandlerT(\d->do pwdbs>返回True)
(\e->返回False))
并检查返回的bool值是否存在异常。

有一个名为的包,它还提供了一个更通用的catch函数:

Control.Exception.Lifted.catch :: 
  (MonadBaseControl IO m, Exception e)
  => m a         -- ^ The computation to run
  -> (e -> m a)  -- ^ Handler to invoke if an exception is raised
  -> m a
{-# LANGUAGE ScopedTypeVariables #-} -- I think this is needed PatternSignatures.
import Control.Exception.Lifted (catch)
import Control.Monad (void)

tryInsert :: Text -> Text -> Text -> Handler ()
tryInsert email user pwd = do 
  pwdbs <- liftIO $ hashedPwd pwd
  (void $ runDB $ insert $ User email user pwdbs) `catch` \(e :: SomeException) -> do
    -- Your exception handling goes code here. This code also lives in the Handler monad.
    return ()
 return ()
存在一个实例MonadBaseControl IO处理程序,因此您可以只使用此函数:

Control.Exception.Lifted.catch :: 
  (MonadBaseControl IO m, Exception e)
  => m a         -- ^ The computation to run
  -> (e -> m a)  -- ^ Handler to invoke if an exception is raised
  -> m a
{-# LANGUAGE ScopedTypeVariables #-} -- I think this is needed PatternSignatures.
import Control.Exception.Lifted (catch)
import Control.Monad (void)

tryInsert :: Text -> Text -> Text -> Handler ()
tryInsert email user pwd = do 
  pwdbs <- liftIO $ hashedPwd pwd
  (void $ runDB $ insert $ User email user pwdbs) `catch` \(e :: SomeException) -> do
    -- Your exception handling goes code here. This code also lives in the Handler monad.
    return ()
 return ()
{-#语言范围的TypeVariables}——我认为这是需要的。
导入控制.Exception.Lifted(catch)
进口管制.单子(无效)
tryInsert::Text->Text->Text->Handler()
tryInsert电子邮件用户pwd=do
普华永道
--您的异常处理将在这里进行。此代码也存在于处理程序monad中。
返回()
返回()

另一种可能性是使用,它还提供了一个通用的catch函数。不过MonadCatchIO mtl不会建立在GHC的基础上。我仍然认为使用
insertUnique
是最干净的处理方法。

您可以在插入之前测试密钥是否唯一,并通过不同的方式处理该情况来避免异常。嗯……YesSOD的新版本中没有checkUnique,但我发现了,谢谢。无论如何,我仍然对异常处理感兴趣。您可以使用
ScopedTypeVariables
语言扩展,然后执行
(\(e::SomeException)->返回False)
谢谢,这也是一个很好的解决方案,您可以避免所有处理程序/未处理程序的内容。是的,在这种情况下,
insertUnique
方法可能是最好的解决方案,但总的来说,这一讨论在将来会很有用,我没有找到关于这个主题的明确信息。
{-# LANGUAGE ScopedTypeVariables #-} -- I think this is needed PatternSignatures.
import Control.Exception.Lifted (catch)
import Control.Monad (void)

tryInsert :: Text -> Text -> Text -> Handler ()
tryInsert email user pwd = do 
  pwdbs <- liftIO $ hashedPwd pwd
  (void $ runDB $ insert $ User email user pwdbs) `catch` \(e :: SomeException) -> do
    -- Your exception handling goes code here. This code also lives in the Handler monad.
    return ()
 return ()