如何在原始服务处理程序中使用IO?

huangapple go评论60阅读模式
英文:

How to use IO in a raw serving handler?

问题

我使用servant 0.19版本,所以没有`RawM`类型。我有一个处理器:

```haskell
hnd :: Something -> ServerT Raw Handler
hnd Something {..} = do
  -- p <- readIORef somePath -- 无法匹配类型"IO b0"与"Tagged Handler Application"
  let stSet = defaultFileServerSettings "a/b/c"
  serveDirectoryWith stSet

该处理器在API的类型中描述为::<|> "some" :> Raw

我想在我的hnd处理器中进行IO操作。如何在这个servant版本中做到这一点(不使用RawM)?此外,如果您可以展示如何使用RawM,我将不胜感激(尽管由于使用的版本0.19,这不是很重要)。


<details>
<summary>英文:</summary>

I use servant 0.19, so no the type `RawM`. I have a handler:

```haskell
hnd :: Something -&gt; ServerT Raw Handler
hnd Something {..} = do
  -- p &lt;- readIORef somePath -- COULDN&#39;T MATCH TYPE &quot;IO b0&quot; WITH &quot;Tagged Handler Application&quot;
  let stSet = defaultFileServerSettings &quot;a/b/c&quot;
  serveDirectoryWith stSet

which is described as an API's type: :&lt;|&gt; &quot;some&quot; :&gt; Raw

I want to do IO operations in my hnd handler. How to do it with this servant version (without RawM)? Also, I'd be grateful if you show it with RawM too (though it is not important due to the used version 0.19).

答案1

得分: 3

Short answer. 试一下:

hnd :: Something -> ServerT Raw Handler
hnd Something{..} = Tagged $ \req resp -> do
  p <- readIORef somePath
  let stSet = defaultFileServerSettings p
  staticApp stSet req resp

The longer answer is that ServerT is a type family associated with the HasServer type class that maps API expressions to transformations of the Handler monad to implement the API :&gt; and :&lt;|&gt; combinators as well as individual endpoints. For a "normal" endpoint, like Get, it maps the type ServerT (Get ...) Handler to plain old Handler.

To implement Raw, ServerT Raw Handler is mapped to Tagged Handler Application. This is simply a WAI Application whose type has been Tagged with the Handler monad.

Servant-supplied wrappers, like serveDirectoryWith, are really just WAI Applications that have been Tagged:

serveDirectoryWith :: StaticSettings -> ServerT Raw m
serveDirectoryWith = Tagged . staticApp

where staticApp is the underlying static file serving Application from the wai-app-static package.

So, that means that the simplified handler:

hnd :: Something -> ServerT Raw Handler
hnd _ = serveDirectoryWith "a/b/c"

is equivalent to:

hnd _ = Tagged (staticApp "a/b/c")

and given that an Application is actually a Request -> (Response -> IO ResponseReceived) -> IO ResponseReceived, this is the same as:

hnd _ = Tagged (\req resp -> staticApp "a/b/c" req resp)

or:

hnd Something{..} = Tagged $ \req resp -> do
  staticApp "a/b/c" req resp

where the do-block is running in the plain old IO monad. In this form, it's now possible to do "normal" IO stuff, which is how I derived the short answer above.

RawM from version 0.20 doesn't gain you much here. It maps ServantT RawM Handler to a modified version of an application:

Request -> (Response -> IO ResponseReceived) -> Handler ResponseReceived

instead of IO, which adds the ability to perform Handler actions besides IO, namely signaling a specific ServerError. If you had a custom HandlerT, it could be more useful. It wouldn't make it any easier to write your hnd, though.

英文:

Short answer. Try:

hnd :: Something -&gt; ServerT Raw Handler
hnd Something{..} = Tagged $ \req resp -&gt; do
  p &lt;- readIORef somePath
  let stSet = defaultFileServerSettings p
  staticApp stSet req resp

The longer answer is that ServerT is a type family associated with the HasServer type class that maps API expressions to transformations of the Handler monad to implement the API :&gt; and :&lt;|&gt; combinators as well as individual endpoints. For a "normal" endpoint, like Get, it maps the type ServerT (Get ...) Handler to plain old Handler.

To implement Raw, ServerT Raw Handler is mapped to Tagged Handler Application. This is simply a WAI Application whose type has been Tagged with the Handler monad.

Servant-supplied wrappers, like serveDirectoryWith, are really just WAI Applications that have been Tagged:

serveDirectoryWith :: StaticSettings -&gt; ServerT Raw m
serveDirectoryWith = Tagged . staticApp

where staticApp is the underlying static file serving Application from the wai-app-static package.

So, that means that the simplified handler:

hnd :: Something -&gt; ServerT Raw Handler
hnd _ = serveDirectoryWith &quot;a/b/c&quot;

is equivalent to:

hnd _ = Tagged (staticApp &quot;a/b/c&quot;)

and given that an Application is actually a Request -&gt; (Response -&gt; IO ResponseReceived) -&gt; IO ResponseReceived, this is the same as:

hnd _ = Tagged (\req resp -&gt; staticApp &quot;a/b/c&quot; req resp)

or:

hnd Something{..} = Tagged $ \req resp -&gt; do
  staticApp &quot;a/b/c&quot; req resp

where the do-block is running in the plain old IO monad. In this form, it's now possible to do "normal" IO stuff, which is how I derived the short answer above.

RawM from version 0.20 doesn't gain you much here. It maps ServantT RawM Handler to a modified version of an application:

Request -&gt; (Response -&gt; IO ResponseReceived) -&gt; Handler ResponseReceived
                                                ^^^^^^^
                                                instead of IO

which adds the ability to perform Handler actions besides IO, namely signaling a specific ServerError. If you had a custom HandlerT, it could be more useful. It wouldn't make it any easier to write your hnd, though.

huangapple
  • 本文由 发表于 2023年7月3日 21:40:44
  • 转载请务必保留本文链接:https://go.coder-hub.com/76605305.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定