处理程序和 Monad

Handler and Monads

我正在努力创作 Servant's HandlerMonad:

class Monad p => Persist p where
  data Configuration p :: *
  runPersistance       :: Configuration p -> p a -> IO a
  initPersistence      :: p ()
  newKind              :: Kind -> p NewKindStatus
  -- ...

kind :: P.Persist p => KindUid -> KindBody -> p (Handler ())
kind (KindUid uid) (KindBody actions) = undefined

subject :: P.Persist p => KindUid -> SubjectUid -> EventData -> p (Handler EventUid)
subject kind subject body = undefined

event = post :<|> get
where post :: P.Persist p => KindUid -> SubjectUid -> Action -> EventData -> p (Handler EventUid)
        post kind subject action body = undefined
        get :: P.Persist p => KindUid -> SubjectUid -> p (Handler [Event])
        get kind subject = undefined

server :: P.Persist p => p (Server HermesAPI)
server = do
    k <- kind
    s <- subject
    e <- event
    return $ k :<|> s :<|> e

app :: P.Persist p => p (Application)
app = serve hermesAPI server

为了回答一条路线,我必须使用 P.Persist,即 MonadIO

我没有找到使server编译的方法:

/app/Main.hs:55:3: error:
    • Couldn't match type ‘KindUid
                        -> SubjectUid -> p0 (Handler [Event])’
                    with ‘(KindUid
                            -> SubjectUid -> Action -> EventData -> Handler EventUid)
                        :<|> (KindUid -> SubjectUid -> Handler [Event])’
    Expected type: p (Server HermesAPI)
        Actual type: p ((KindUid -> KindBody -> Handler ())
                        :<|> ((KindUid -> SubjectUid -> EventData -> Handler EventUid)
                            :<|> (KindUid -> SubjectUid -> p0 (Handler [Event]))))
    • In a stmt of a 'do' block: k <- kind
    In the expression:
        do k <- kind
        s <- subject
        e <- event
        return $ k :<|> s :<|> e
    In an equation for ‘server’:
        server
            = do k <- kind
                s <- subject
                e <- event
                ....
   |
55 |   k <- kind
   |   ^^^^^^^^^

app/Main.hs:55:8: error:
    • Couldn't match type ‘KindBody’ with ‘KindUid’
    Expected type: p (KindUid -> KindBody -> Handler ())
        Actual type: KindUid -> KindBody -> KindBody -> Handler ()
    • In a stmt of a 'do' block: k <- kind
    In the expression:
        do k <- kind
        s <- subject
        e <- event
        return $ k :<|> s :<|> e
    In an equation for ‘server’:
        server
            = do k <- kind
                s <- subject
                e <- event
                ....
   |
55 |   k <- kind
   |        ^^^^

因为 Handler 已经是 newtype,我卡住了。 我如何组合 Handler 和我的 Monad 以便 运行 它在 Servant 中?

编辑:

感谢 Mark Seemann,我做了一个步骤,提升 Handler 以利用 ServerT:

newtype LiftHandler p a = LiftHandler (p (Handler a))
server :: P.Persist p => ServerT HermesAPI (LiftHandler p)
server = LiftHandler kind :<|> LiftHandler subject :<|> LiftHandler event

导致:

app/Main.hs:55:22: error:
    • Couldn't match type ‘KindBody -> p2 (Handler ())’
                    with ‘Handler a2’
    Expected type: KindUid -> Handler a2
        Actual type: KindUid -> KindBody -> p2 (Handler ())
    • Probable cause: ‘kind’ is applied to too few arguments
        In the first argument of ‘LiftHandler’, namely ‘kind’
        In the first argument of ‘(:<|>)’, namely ‘LiftHandler kind’
        In the expression:
            LiftHandler kind :<|> LiftHandler subject :<|> LiftHandler event
   |
55 | server = LiftHandler kind :<|> LiftHandler subject :<|> LiftHandler event

谢谢。

这只是解决方案的草图,但您可能会发现这些提示很有用。 HandlernewtypeExceptT ServerError IO a 的包装,但有时,您可能无法直接生成这样的类型。

但是,如果您有另一个 Monad(比如您的 Persist),您可以先使用那个 Monad 定义您的 API,然后使用 hoistServer 转换为 ExceptT ServerError IO a.

因此,想象一下,例如,您使用以下类型定义 server

server :: ServerT API P.Persist

您应该可以像这样将其转换为 Server 和 运行:

run port $ serve api $ hoistServer api (Handler . trans) $ server

其中 trans 是您从 P.PersistExceptT ServerError IO 的转变。

正如 hoistServerthe documentation 所说:

Sometimes our cherished Handler monad isn't quite the type you'd like for your handlers.

我没有尝试编译我的任何代码草图,因此可能存在类型错误。我不知道 P.Persist 是什么,所以我无法重现这个问题。

您也可以将 server 定义为

server :: ServerT API (ExceptT ServantErr P.Persist)

以便您可以使用 throwError 传达 HTTP(错误)状态代码。这也可能使编写从 ExceptT ServantErr P.PersistExceptT ServerError IO 的转换变得更容易,因为现在你只需要弄清楚如何将 P.Persist 转换为 IO.