网络编程时我遇到的事情很多:我想运行一个有可能失败的操作.在失败时,我想向客户端发送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
Run Code Online (Sandbox Code Playgroud)
因为错误是例外,我不喜欢我需要所有这些案例只是为了抓住它们.只要有任何东西,我想做同样的事情.有没有办法在一行上用类似的东西表达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)
}
Run Code Online (Sandbox Code Playgroud)
所以,我可以写一些样板文件,但是我不希望错误让我的嵌套变得混乱,因为只是想继续使用找到的案例更常见.
最干净的方法是什么?我在理论上知道我可以使用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
Run Code Online (Sandbox Code Playgroud)
以下是我用来确保所有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
Run Code Online (Sandbox Code Playgroud)
如果我这样做完全错了那么请有人喊出来.这可能是明智的,如果采取这种做法,修改的类型签名databaseCall和anotherDatabaseCall也使用ErrorT,这样a <- ErrorT b可以减少到a <- b在doSomeWebStuff'.
由于我是一个完整的菜鸟ErrorT,除了"这里有一些代码,还有一些乐趣"之外,我不能真正做任何手工操作.
不是你问题的直接答案,但是你考虑过使用Snap吗?在快照中,我们内置了一个惯用的短路行为:
getResponse >>= finishWith
Run Code Online (Sandbox Code Playgroud)
哪里
finishWith :: MonadSnap m => Response -> m a
Run Code Online (Sandbox Code Playgroud)
因此,给定一个响应对象,它将提前终止(并匹配之后的任何类型).Haskell laziness将确保在finishWith之后不会执行Snap monad中的计算.
我有时会做一个小帮手:
finishEarly code str = do
modifyResponse $ setResponseStatus code str
modifyResponse $ addHeader "Content-Type" "text/plain"
writeBS str
getResponse >>= finishWith
Run Code Online (Sandbox Code Playgroud)
然后我可以在我的处理程序中的任何地方使用它.
myHandler = do
x <- doSomething
when (x == blah) $ finishEarly 400 "That doesn't work!!"
doOtherStuff
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
305 次 |
| 最近记录: |