简体   繁体   English

Haskell与多个monad绑定

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM