需要澄清令人困惑的 Http4s 消息类型 `Response[F]` / `Request[F]`

Maa*_*mon 4 functional-programming scala http4s

我很难理解为什么RequestResponse在F中参数化。

类似的东西是猫效应数据类型资源。

从文档中

https://typelevel.org/cats-effect/docs/std/resource

我们发现下面的定义

object Resource {
  def make[F[_], A](acquire: F[A])(release: A => F[Unit]): Resource[F, A]

  def eval[F[_], A](fa: F[A]): Resource[F, A]
}

abstract class Resource[F, A] {
  def use[B](f: A => F[B]): F[B]
}
Run Code Online (Sandbox Code Playgroud)

尤其

def use[B](f: A => F[B]): F[B]明确了为什么 Resource 在 F 中参数化。

鉴于文档中没有任何内容解释 Response[F] (请注意,我非常理解为什么 F[Response],它是我不理解的内部 F),我查看了一些代码https:// /github.com/http4s/http4s/blob/main/core/src/main/scala/org/http4s/Message.scala

除非我没有仔细观察,否则我找不到任何证明效果类型存在的理由。

有人可以解释一下内部 F 参数吗?

以与https://www.haskellforall.com/2013/06/the-resource-applicative.html类似的方式

Resource 是一个 IO 操作,它获取 a 类型的某些资源,并返回释放该资源的 IO () 类型的终结器。您可以将 a 视为句柄,但它实际上可以是任何可以获取或释放的东西,例如 Socket 或 AMQP 连接。

我们能否对什么是响应及其作用有一个概念性定义,这确实需要它在特定效果类型上进行参数化?

Yuv*_*kov 6

让我们看看 的定义Http[F, G],它是 的核心http4s

/** A kleisli with a [[Request]] input and a [[Response]] output.  This type
  * is useful for writing middleware that are polymorphic over the return
  * type F.
  *
  * @tparam F the effect type in which the [[Response]] is returned
  * @tparam G the effect type of the [[Request]] and [[Response]] bodies
  */
type Http[F[_], G[_]] = Kleisli[F, Request[G], Response[G]]
Run Code Online (Sandbox Code Playgroud)

Kleisli本质上是一个有效函数的包装A => F[B]::

final case class Kleisli[F[_], -A, B](run: A => F[B])
Run Code Online (Sandbox Code Playgroud)

如果我们在这里开发类型俄罗斯方块,我们会看到 Http 的实际类型签名是:

Request[G] => F[Response[G]]
Run Code Online (Sandbox Code Playgroud)

Request现在,和Response被参数化的原因G是它们可能包含一个主体。我们从这两个定义中都可以看出这一点:

final class Request[F[_]](
    val method: Method = Method.GET,
    val uri: Uri = Uri(path = "/"),
    val httpVersion: HttpVersion = HttpVersion.`HTTP/1.1`,
    val headers: Headers = Headers.empty,
    val body: EntityBody[F] = EmptyBody,
    val attributes: Vault = Vault.empty

final case class Response[F[_]](
    status: Status = Status.Ok,
    httpVersion: HttpVersion = HttpVersion.`HTTP/1.1`,
    headers: Headers = Headers.empty,
    body: EntityBody[F] = EmptyBody,
    attributes: Vault = Vault.empty)
    extends Message[F] {
Run Code Online (Sandbox Code Playgroud)

您可以看到 theF用于 the EntityBody[F],它本身就是 a 的类型别名Stream[F, Byte],用于延迟消耗 Effect 中的输入/输出流F

具体来说HttpRoutes[F],两个类型参数实际上是相同的:

type HttpRoutes[F[_]] = Http[OptionT[F, *], F]
Run Code Online (Sandbox Code Playgroud)

这确实是:

Request[F] => F[Option[Response[[F]]]
Run Code Online (Sandbox Code Playgroud)

因此,我们F[Response[F]]到处都看到而不是有一个单独的类型参数体。

总而言之,外部F输入F[Response[G]]用于捕获生成响应可能是有效操作的事实。这就是为什么F通常是某种 IO 类型(cats-effectIOZIO[R, E, A]),并且请求/响应中的内部G用于对在给定效果中生成字节的流进行建模。

  • 这个想法是,http4s 不必“提前”评估正文来构建请求/响应。例如,http4s 在解析 HTTP 标头之后但在接收整个正文之前将“Request”传递给您的服务 (https://http4s.org/v0.21/entity/)。如果你想读取正文,你就“消耗”流,即。将其评估为“F[Array[Byte]]”、“F[String]”或类似的值。 (2认同)
  • @MaatDeamon 在非常狭义上可以这么说,但实际上 HTTP 请求和响应不仅仅是正文内容的字节。它们是标头、查询参数,某些请求类型根本没有正文。它们一起组成了请求和响应实体。 (2认同)