Haskell Servant - serveWithContext 的目的是什么?它有什么 ReaderT 做不到的?

Haskell Servant - What is the Purpose of serveWithContext and What Does it do That a ReaderT Can't?

我正在尝试了解 Servant 的 serveWithContext 函数的用途。文档指出它不是 ReaderT Monad 的替代品,但我不确定它试图解决 ReaderT 尚未解决的问题。

例如,这是来自 servant-auth GitHub 页面上的示例:

unprotected :: CookieSettings -> JWTSettings -> Server Unprotected
unprotected cs jwts = checkCreds cs jwts :<|> serveDirectory "example/static"

server :: CookieSettings -> JWTSettings -> Server (API auths)
server cs jwts = protected :<|> unprotected cs jwts
let jwtCfg = defaultJWTSettings myKey
      cfg = defaultCookieSettings :. jwtCfg :. EmptyContext
      api = Proxy :: Proxy (API '[JWT])
  _ <- forkIO $ run 7249 $ serveWithContext api cfg (server defaultCookieSettings jwtCfg)

似乎 serveWithContext 被用来将 Cookie 和 JWT 设置传递给处理程序,但我不明白为什么这不能通过 ReaderT 完成。此外,serveWithContext 似乎将这些值传递两次:一次作为绑定到 cfg 的上下文对象,另一次作为函数调用中的参数 server defaultCookieSettings jwtCfg.

如果有人能为我揭开 Servant 的上下文类型的神秘面纱,我将不胜感激。

Servant 的机制似乎没有对您选择在其中定义处理程序的基础 monad 做出假设。这意味着它不能强迫你选择任何特定的 monad(比如 ReaderT)来响应路由中存在的某些组合器,并且它不会对你选择的 monad 做出“反应”以便启用某些行为。

在servant-auth的情况下,我们需要向servant提供一些extra information关于如何处理cookie之类的。

Context 系统在 route, hoistServerWithContext and serveWithContext 的位置参数中收集额外信息,同时仍然让您选择您想要的任何 monad。参数的确切类型取决于 API.

中存在的路由组合器

servant tutorial 有一些关于 Context 的段落:

When someone makes a request to our "private" API, we’re going to need to provide to servant the logic for validating usernames and passwords. [...] Servant 0.5 introduced Context to handle this. [...] the idea is simple: provide some data to the serve function, and that data is propagated to the functions that handle each combinator.

至于

Furthermore, serveWithContext appears to be passing these values in twice

我不确定,但我怀疑 checkCredscsjwts 作为参数只是作为一个例子,说明如果纯粹在处理程序中完成身份验证将如何执行,没有 Servant 本身的帮助。相比之下,protected 端点已经接收到身份验证的 result 作为参数;它不必自己执行。

在实际应用程序中,server 不会采用这些参数,它们只会在 Context 中传递。