Ho iniziato a utilizzare Yesod per sviluppare un piccolo progetto, questa è la prima volta che uso Haskell per fare qualcosa di reale. Questo codice che gestisce un modulo di registrazione funziona bene:Haskell: gestione delle eccezioni in non-IO monadi
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()
Ora il problema è: se firmo in due volte con le stesse credenziali ottengo una InternalServerError
. Questo è giusto, perché nella configurazione del mio modello c'è UniqueUser email username
. Quindi mi piacerebbe prendere e gestire questo errore in qualche modo. Come posso farlo e, in generale, come funziona la gestione delle eccezioni in Haskell quando si hanno a che fare con le monade non-IO definite in una libreria o in un framework esterno?
PS: Ho letto il tutorial this, ma ciò è utile se state progettando una nuova libreria. Ho provato a utilizzare la funzione catch, ma ho ricevuto molti errori di tipo.
Modifica
Grazie Ankur, il codice ha lavorato con una piccola modifica, per eliminare questo errore:
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)
codice:
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))
Con ScopedTypeVariables
estensione abilitata
Edit 2
versione finale, dopo il suggerimento 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)
È possibile utilizzare [checkUnique] (http://hackage.haskell.org/packages/archive/persistent/0.3.1.3/doc/html/Database-Persist.html#v:checkUnique) per verificare se la chiave è unico prima di inserire ed evitare l'eccezione gestendo diversamente quella causa. – bennofs
Umh ... non c'è checkUnique nelle versioni più recenti di Yesod, ma ho trovato [insertUnique] (http://hackage.haskell.org/packages/archive/persistent/latest/doc/html/Database-Persist-Class .html # v: insertUnique), grazie. Ad ogni modo sono ancora interessato alla gestione delle eccezioni. – andrebask
Puoi usare l'estensione di linguaggio 'ScopedTypeVariables' e poi fare' (\ (e :: SomeException) -> return False) ' – Ankur