Right now my code looks like this:
postUser :: ServerPart Response
postUser = do
-- parseBody :: ServerPart (Maybe User)
parsedUser <- parseBody
case parsedUser of
Just user -> do
-- saveUser :: User -> ServerPart (Maybe (Key User))
savedUser <- saveUser user
case savedUser of
Just user -> successResponse
Nothing -> errorResponse "user already exists"
Nothing -> errorResponse "couldn't parse user"
Which works, but I know there's a way to avoid the nested pattern matching. I thought this was what bind would do for me, so I tried
parseUser :: ServerPart (Maybe User)
addUser :: User -> ServerPart (Maybe ())
case parseUser >>= saveUser of
Just _ -> success
Nothing -> error
and
savedUser <- (parseUser >>= saveUser)
case savedUser of
Just _ -> success
Nothing -> error
But I get the following error:
Couldn't match type ‘Maybe a0’ with ‘User’
Expected type: Maybe a0 -> ServerPartT IO (Maybe (Key User))
Actual type: User -> ServerPart (Maybe (Key User))
In the second argument of ‘(>>=)’, namely ‘saveUser’
In the expression: parseBody >>= saveUser
which I take to mean >>=
is applying saveUser
to the Maybe User
instead of the User
that I need it to, and I'm not sure how to finagle the types to match. How can I rewrite this to avoid the nested pattern matching?
While I would argue that the original way you have it written is the most readable approach, taking this as an exercise, the MaybeT monad transformer is what you are looking for.
The problem you are running into is that your are trying to jump between the ServerPart monad and the Maybe monad. This is why you can't directly bind parseBody and saveUser. Monad transformers allow you to combine monads to avoid this problem.
import Control.Monad.Trans.Maybe
postUser :: ServerPart Response
postUser = do
-- parseBody :: MaybeT ServerPart User
-- saveUser :: User -> MaybeT ServerPart (Key User)
user <- runMaybeT $ parseBody >>= saveUser
case user of
Just _ -> successResponse
Nothing -> errorResponse "Error saving user"
You will need to refactor your parseBody and saveUser functions to use the MaybeT monad. Since I can't see these functions, I can't help you there, but it can usually be done easily using lift
from Control.Applicative.
Useful links on monad transformers:
https://en.wikibooks.org/wiki/Haskell/Monad_transformers https://www.schoolofhaskell.com/user/commercial/content/monad-transformers
EDIT: MaybeT for parseBody
parseBody :: FromJSON a => MaybeT ServerPart a
parseBody = MaybeT $ fmap A.decode getBody
And just as a general tip: bar >>= (return . f)
is equivalent to fmap f bar
. The latter being cleaner and more general since it does not require the monad instance.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.