2012-01-27 29 views
6

Điều gì đó xảy ra với tôi rất nhiều trong khi lập trình web: Tôi muốn chạy một thao tác có khả năng bị lỗi. Khi thất bại, tôi muốn gửi cho khách hàng 500. Thông thường, tôi chỉ muốn tiếp tục thực hiện một loạt các bước.Cách "thoát sớm" trong một trang web đơn lẻ

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 

vì các trường hợp ngoại lệ là ngoại lệ, tôi không thích rằng tôi cần tất cả những thứ đó chỉ để bắt chúng. Tôi muốn làm điều tương tự bất cứ khi nào bất cứ điều gì là một trái. Có cách nào để thể hiện rằng trên một dòng với một cái gì đó như guard, nhưng kiểm soát những gì nó trả về trên một lối ra?

Trong ngôn ngữ khác tôi có thể làm điều này:

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

Vì vậy, tôi ok viết một số soạn sẵn, nhưng tôi không muốn các lỗi để gây rối với làm tổ của tôi, khi nó phổ biến hơn để chỉ muốn tiếp tục chuyển tiếp với trường hợp đã tìm thấy.

Cách sạch nhất để thực hiện việc này là gì? Tôi biết về mặt lý thuyết, tôi có thể sử dụng một đơn nguyên để thoát sớm khi thất bại, nhưng tôi chỉ thấy các ví dụ với Maybe và nó sẽ trả lại Nothing ở cuối, thay vì cho phép tôi chỉ định những gì nó trả về.

+1

Ví dụ điển hình về lý do luật Monad hữu ích :) –

+0

Có thể sử dụng đơn vị 'Hoặc 'để thoát sớm với giá trị trả lại (' Trái'). – shang

+1

@shang Ngày nay '' e'' của 'e' là' lỗi', có khả năng sẽ tạo ra một vài hiệu ứng không mong muốn.Tôi khuyên bạn nên xem xét 'ErrorT'. –

Trả lời

6

Đây là cách tôi sẽ làm điều đó với ErrorT. Disclaimer: Tôi chưa bao giờ thực sự sử dụng ErrorT trước đây.

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 

Dưới đây là hàng nhập khẩu và tờ khai loại I dùng để đảm bảo tất cả typechecks một cách chính xác:

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 

Nếu tôi đang làm điều này hoàn toàn sai thì xin vui lòng, ai đó hét to. Có thể là khôn ngoan, nếu bạn thực hiện phương pháp này, để sửa đổi chữ ký loại databaseCallanotherDatabaseCall cũng sử dụng ErrorT, cách đó a <- ErrorT b có thể được giảm xuống a <- b trong doSomeWebStuff'.

Vì tôi là một noob hoàn chỉnh tại ErrorT, tôi thực sự không thể thực hiện bất kỳ thao tác nào ngoài "đây là một số mã, hãy vui vẻ".

5

Không phải là câu trả lời trực tiếp cho câu hỏi của bạn, nhưng bạn đã cân nhắc sử dụng Snap chưa? Trong tích tắc, chúng ta có ngắn mạch hành vi tích hợp với một thành ngữ:

getResponse >>= finishWith 

nơi

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

Vì vậy, cho một đối tượng phản ứng, nó sẽ chấm dứt sớm (và phù hợp với bất cứ loại được đưa ra sau đó) . Haskell laziness sẽ đảm bảo tính toán trong Snap monad sau khi finishWith sẽ không được thực thi.

đôi khi tôi thực hiện một chút helper:

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

mà tôi sau đó có thể sử dụng bất cứ nơi nào trong xử lý của tôi.

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

+1 chắc chắn có liên quan; Tôi sẽ đề cập đến trong câu trả lời của tôi rằng các monome khung chính trên web có thể có một số dạng ErrorT được tích hợp sẵn, nhưng tôi không đủ quen thuộc với bất kỳ để chứng minh rằng yêu cầu như bạn có. –

+0

Tôi đang sử dụng scotty, và thực sự nó có cấu trúc giống như kết thúc –

Các vấn đề liên quan