2012-01-27 2 views
6

웹 프로그래밍 중에 나에게 많은 일이 일어납니다. 실패 할 가능성이있는 작업을 실행하고 싶습니다. 실패하면 클라이언트에게 500을 보내려고합니다. 일반적으로 일련의 단계를 계속 수행하기를 원합니다.웹 모나드에서 "빠른 탈출"방법

doSomeWebStuff :: SomeWebMonad() 
doSomeWebStuff = do 
    res <- databaseCall 
    case res of 
     Left err -> status 500 
     Right val -> do 
      res2 <- anotherDatabaseCall (someprop val) 
      case res2 of 
       Left err -> status 500 
       Right val2 -> text $ show val2 

오류가 예외이기 때문에 나는 그 모든 것들을 잡으려고하고 싶지 않다. 왼쪽이 될 때마다 똑같은 일을하고 싶습니다. 한 줄에 guard 같은 것을 표현할 수있는 방법이 있습니까?하지만 출구에서 반환되는 내용을 제어 할 수 있습니까? 다른 언어에서

나는이 작업을 수행 할 수 있습니다 :

function doSomeWebStuff() { 
    var res = databaseCall() 
    if (res == Error) return status 500 
    var res2 = anotherDatabaseCall(res.someprop) 
    if (res2 == Error) return status 500 
    return text(res2) 
} 

그래서, 나는 몇 가지 상용구를 작성 확인 해요,하지만 그것은 단지에 훨씬 더 흔한 일 때, 내 중첩 엉망으로 오류를 원하지 않는다 발견 된 사건으로 계속 전진하고 싶다.

가장 확실한 방법은 무엇입니까? 나는 이론 상 모나드를 사용하여 실패 초기에 종료 할 수 있지만, 단지 Maybe으로 예제를 보았고 반환 할 내용을 지정하는 대신 끝에 Nothing을 반환합니다.

+1

모나드 법률이 유용한 이유의 아름다운 예 : –

+0

은'Either' 모나드는 반환 값 ('Left') 조기 종료에 사용할 수 있습니다. – shang

+1

@shang 요즘에는 '어느 쪽인가'의'실패 '가'오류'인데, 이는 원치 않는 결과를 낳을 수 있습니다.나는'ErrorT'를 보길 권합니다. –

답변

6

다음은 ErrorT으로 어떻게 처리할까요? 면책 조항 : 전에 실제로 ErrorT을 사용한 적이 없습니다. 여기

webStuffOr500 :: ErrorT String SomeWebMonad() -> SomeWebMonad() 
webStuffOr500 action = do 
    res <- runErrorT action 
    case res of 
    Left err -> do 
     logError err -- you probably want to know what went wrong 
     status 500 
    Right() -> return() 

doSomeWebStuff :: SomeWebMonad() 
doSomeWebStuff = webStuffOr500 doSomeWebStuff' 

doSomeWebStuff' :: ErrorT String SomeWebMonad() 
doSomeWebStuff' = do 
    val <- ErrorT databaseCall 
    val2 <- ErrorT $ anotherDatabaseCall (someprop val) 
    lift $ text $ show val2 

내가 제대로 확인 모든 typechecks를 만들기 위해 사용되는 수입과 유형 선언은 다음과 같습니다

import Control.Monad.Identity 
import Control.Monad.Error 
import Control.Monad.Trans (lift) 
import Control.Monad 

type SomeWebMonad = Identity 

data Foo = Foo 
data Bar = Bar 
data Baz = Baz deriving (Show) 

someprop :: Foo -> Bar 
someprop = undefined 
databaseCall :: SomeWebMonad (Either String Foo) 
databaseCall = undefined 
anotherDatabaseCall :: Bar -> SomeWebMonad (Either String Baz) 
anotherDatabaseCall = undefined 
logError :: String -> SomeWebMonad() 
logError = undefined 
text :: String -> SomeWebMonad() 
text = undefined 
status :: Int -> SomeWebMonad() 
status = undefined 

나는이 모든 잘못 후, 누군가가 외치십시오하고있어합니다. 이러한 접근 방식을 취하는 경우 및 anotherDatabaseCall의 형식 서명을 ErrorT으로 수정하면 a <- ErrorT bdoSomeWebStuff'a <- b으로 줄일 수 있습니다.

나는 완벽한 멍청한 사람 ErrorT이므로, "여기에 몇 가지 코드가 있는데 재미있게 간다"외에 손을 잡을 수는 없습니다.

5

질문에 대한 직접적인 대답은 아니지만 Snap을 사용해 보셨습니까? 스냅에서, 우리는 단락 행동이 내장 된 관용적으로 가지고가 종료됩니다,

finishWith :: MonadSnap m => Response -> m a 

그래서 응답 객체를 주어진

getResponse >>= finishWith 

를 초기 (그 다음에 오는 어떤 유형 일치) . Haskell 게으름은 Snap 모나드 내에서 계산 완료를 보장합니다. 실행되지 않습니다.

finishEarly code str = do 
    modifyResponse $ setResponseStatus code str 
    modifyResponse $ addHeader "Content-Type" "text/plain" 
    writeBS str 
    getResponse >>= finishWith 

그때 내 핸들러 어디서나 사용할 수 있습니다

는 가끔 약간의 도우미를합니다.

myHandler = do 
    x <- doSomething 
    when (x == blah) $ finishEarly 400 "That doesn't work!!" 
    doOtherStuff 
+1

+1 확실히 관련성이 있습니다. 내 대답은 주요 웹 프레임 워크 모나드에 ErrorT가 내장되어있을 가능성이 높다고 언급했지만 필자는 그 같은 주장을 입증하기에는 익숙하지 않았다. –

+0

나는 scotty를 사용하고 있습니다. 실제로는 finishEarly와 비슷한 구조를 가지고 있습니다. –

관련 문제