[英]Haskell bind with multiple monads
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
我以为这就是bind对我的影响,所以我尝试了
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. 我的意思是
>>=
正在将saveUser
应用于Maybe User
而不是我需要的User
,而且我不确定如何确定类型以匹配。 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. 尽管我认为原始的编写方式是最易读的方法,但作为练习,MaybeT monad转换器正是您所需要的。
The problem you are running into is that your are trying to jump between the ServerPart monad and the Maybe monad. 您遇到的问题是您正在尝试在ServerPart monad和Maybe monad之间跳转。 This is why you can't directly bind parseBody and saveUser.
这就是为什么您不能直接绑定parseBody和saveUser的原因。 Monad transformers allow you to combine monads to avoid this problem.
Monad变压器使您可以组合Monad来避免此问题。
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. 您将需要重构parseBody和saveUser函数以使用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. 由于看不到这些功能,因此无法为您提供帮助,但是通常可以使用Control.Applicative中的
lift
轻松完成此操作。
Useful links on monad transformers: monad变压器上的有用链接:
https://en.wikibooks.org/wiki/Haskell/Monad_transformers https://www.schoolofhaskell.com/user/commercial/content/monad-transformers https://zh.wikibooks.org/wiki/Haskell/Monad_transformers https://www.schoolofhaskell.com/user/commercial/content/monad-transformers
EDIT: MaybeT for parseBody 编辑:也许是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
. 就像一般提示:
bar >>= (return . f)
fmap f bar
bar >>= (return . f)
等效于fmap f bar
。 The latter being cleaner and more general since it does not require the monad instance. 后者更干净,更通用,因为它不需要monad实例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.