在进行Web编程时,很多事情发生在我身上:我想运行一个可能会失败的操作。发生故障时,我想向客户端发送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

由于错误是异常(exception),因此我不希望我需要所有这些案例内容来捕获它们。每当剩下的东西我都想做同样的事情。有没有一种方法可以像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)
}

因此,我可以编写一些样板文件,但我不想让错误弄乱我的嵌套,而在这种情况下,只想继续处理发现的情况就更常见了。

什么是最干净的方法?从理论上讲,我知道我可以使用monad在失败时提前退出,但是我只看到了带有Maybe的示例,它会在最后返回Nothing,而不是让我指定它返回的内容。

最佳答案

这是我要使用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

这是我用来确保所有类型检查均正确的导入和类型声明:
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

如果我做错了,那么请大声喊叫。如果采用这种方法,将databaseCallanotherDatabaseCall的类型签名修改为也使用ErrorT可能是明智的,这样就可以将a <- ErrorT b简化为a <- b中的doSomeWebStuff'

由于我是ErrorT的完全菜鸟,所以除了“这里有一些代码,去找点乐子”之外,我真的无法做任何事情。

关于haskell - 如何在Web Monad中 “escape early”,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/9034176/

10-09 17:09