How to "run away early" in the web monad

Something that happens to me during web programming: I want to start an operation that has a chance of failure. If I refuse, I want to send 500 to the client. Usually, I just want to continue with a series of steps.

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 

since errors are exceptions, I don’t like that I need all these things to catch them. I want to do the same when something is left. Is there a way to express this on the same line as something like guard , but control what it returns in the output?

In another language, I could do this:

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

So, I am good at writing some kind of template, but I don’t want errors messing around with my socket when it is much more common, just to continue working with the case found.

What is the cleanest way to do this? I know that theoretically I can use the monad to go out at an early stage of failure, but I only saw examples with Maybe , and it would return Nothing at the end, instead of letting me indicate what it returns.

+6
source share
2 answers

Here is how I would do it with ErrorT . Disclaimer: I have never used 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 

Here are the import and type declarations that I used to make sure that all typechecks are correct:

 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 

If I do it all wrong, then please someone scream. It might be prudent if you take this approach to change the signature type databaseCall and anotherDatabaseCall to also use ErrorT , so a <- ErrorT b can be reduced to a <- b in doSomeWebStuff' .

Since I am a complete noob in ErrorT , I can’t do any manual work at all except “here is some code, go, have some fun”.

+6
source

Not a direct answer to your question, but have you considered using Snap ? In the snap, we have a short-circuited behavior, built-in with idiomatic:

 getResponse >>= finishWith 

Where

 finishWith :: MonadSnap m => Response -> ma 

Therefore, given the object of the response, it will be broken at an early stage (and after that any type will correspond). Dexterity Haskell will provide calculations in Monap Monad after completion. This will not be done.

I sometimes make a little helper:

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

which I can then use anywhere in my handlers.

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

Source: https://habr.com/ru/post/907024/


All Articles