我看到很多F[Request[F]]
,F[Monitor[F]]
和F[Something[F]]
周围。尽管我了解如何处理此问题的机制,但我无法直观地理解为什么某些效果 ( F
) 应该像这样出现(既环绕Something
又出现在里面Something
)。洞察力?
2 回答
一个简单的 HTTP 服务器可以用以下函数表示:Request => Response
但是,我们通常需要执行有效的操作,例如从数据库中检索数据或调用外部服务。这种情况我们可以这样定义:Request => F[Response]
http4s 对称为 HttpRoutes[F] 的路由(端点)有自己的抽象,它是文档中提到的 Kleisli[OptionT[F, ?], Request, Response] 的类型别名。
正如我们从猫文档中知道的那样,Kleisli 只是返回单子值的函数的组合,例如 Option[A]、Either[L, R] 等。
函数最有用的属性之一是它们可以组合。也就是说,给定一个函数 A => B 和一个函数 B => C,我们可以将它们组合起来创建一个新的函数 A => C。
在这种情况下,http4s 的 Kleislei 接受 Request 并返回 F[Response],只需 Request => F[Response]。F 是一种效果类型。
通过对上述请求和响应类型进行一些修改,我们得到以下内容:
type HttpRoutes[F] = Kleisli[OptionT[F, *], Request[F], Response[F]]
HttpRoutes[F] 被声明为一系列简单的 case 语句。每个 case 语句都尝试匹配并可选地从传入的 Request[F] 中提取。与第一个匹配案例相关的代码用于生成 F[Response[F]]。
最简单的 case 语句匹配所有请求而不提取任何内容。请求的右侧必须返回 F[Response[F]]。
我们可以在这里看到 F[Response[F]] 的简单演示
scala> val getRoot = Request[IO](Method.GET, uri"/")
getRoot: org.http4s.Request[cats.effect.IO] = Request(method=GET, uri=/, headers=Headers())
scala> val io = service.orNotFound.run(getRoot)
io: cats.effect.IO[org.http4s.Response[cats.effect.IO]] = <function1>
这里cats.effect.IO[org.http4s.Response[cats.effect.IO]]
还没有解决,这是异步结果。它将在将来的某个时候以简单的 Future/Promise 解决。我们可以强制它运行:
scala> val response = io.unsafeRunSync
response: org.http4s.Response[cats.effect.IO] = Response(status=200, headers=Headers())
F[_] 只是一个抽象。它可以是 List 或 IO 类型为 * -> * 的其他东西。您可以定义常用功能的主要思想。一个很好的例子是 Functor 类型类和这个类中的 map 函数。所以在你的代码中,你可以说你 F 是一个仿函数 (F[_]: Functor) 并且在它之后允许你使用一个映射函数。