2013-08-17 1 views
6

작은 프로젝트를 개발하기 위해 Yesod를 사용하기 시작했습니다. 하스켈을 사용하여 실제 작업을 수행 한 것은 이번이 처음입니다. 등록 양식을 처리 이 코드는 잘 작동 : 하스켈 : non-IO 모나드에서의 예외 처리

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() 

지금 문제가되고 : 나도 같은 자격 증명을 두 번 로그인하면 내가 InternalServerError를 얻을. 내 모델 구성에 UniqueUser email username이 있기 때문에 이것이 옳습니다. 그래서 나는이 오류를 어떻게 든 잡아서 다루기를 원합니다. 외부 라이브러리 나 프레임 워크에 정의 된 non-IO 모나드를 처리 할 때 하스켈에서 예외 처리가 어떻게 작동하는지 일반적으로 어떻게 할 수 있습니까?

추신 : this 튜토리얼을 읽었지만 새 라이브러리를 디자인 할 때 유용합니다. catch 함수를 사용하려고했지만 형식 오류가 많습니다. ScopedTypeVariables 확장

을 활성화

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)) 

으로 :

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) 

코드 : 편집

당신에게 Ankur 감사, 당신의 코드는이 오류를 제거하기 위해, 약간의 수정과 협력

편집 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) 
+2

[checkUnique] (http://hackage.haskell.org/packages/archive/persistent/0.3.1.3/doc/html/Database-Persist.html#v:checkUnique)를 사용하여 키가 맞는지 테스트 할 수 있습니다. 삽입하기 전에 고유해야하며 해당 케이스를 다르게 처리하여 예외를 피하십시오. – bennofs

+0

음 ... Yesod의 최신 버전에는 checkUnique가 없습니다. 그러나 [insertUnique] (http://hackage.haskell.org/packages/archive/persistent/latest/doc/html/Database-Persist-Class)를 발견했습니다. .html # v : insertUnique), 감사합니다. 어쨌든 나는 아직도 예외 처리에 관심이있다. – andrebask

+1

'ScopedTypeVariables' 언어 확장을 사용하고'(\ (e :: SomeException) -> return False)' – Ankur

답변

3

당신은 아래 그림과 같은 것을 시도 할 수 있습니다, 기본적으로 Handler 모나드 트랜스포머 HandlerT이다 (나는 아래의 코드를 확인 입력하지 않은 :))

tryInsert :: Text -> Text -> Text -> Handler Bool 
tryInsert email user pwd = HandlerT (\d -> do pwdbs <- hashedPwd pwd 
               catch (unHandlerT (runDB $ insert $ User email user pwdbs) d >> return True) 
                (\e -> return False)) 

예외가 있거나없는 경우 반환 된 bool 값을 확인하십시오.

{-# 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() 

: 당신은 그냥이 기능을 사용할 수 있도록

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 

는, 인스턴스 MonadBaseControl IO 핸들러가 존재한다 :

7

도 더 일반적인 캐치 기능을 제공 lifted-base라는 패키지가 또 다른 가능성은 일반적인 catch 기능을 제공하는 MonadCatchIO-mtl을 사용하는 것입니다. MonadCatchIO-mtl은 GHC HEAD에서 빌드되지 않습니다. 나는 또한 여전히 이것을 사용하는 것이 가장 깨끗한 방법이라고 생각한다 (insertUnique).

+0

고마워요, 이것도 좋은 해결책입니다. 모든 Handler/unHandler 항목을 피할 수 있습니다. 예,'insertUnique' 방법이 아마도이 경우 가장 좋은 해결책 일 것입니다. 그러나 일반적으로이 토론은 앞으로 유용 할 것이고, 주제에 대한 많은 정보를 찾지 못했습니다. – andrebask