我有一个API,它看起来像这样:
object Comics {
...
def impl[F[_]: Applicative]: Comics[F] = new Comics[F] {
def getAuthor(slug: Authors.Slug): F[Option[Authors.Author]] =
...
和一个看起来像这样的路由:
object Routes {
def comicsRoutes[F[_]: Sync](comics: Comics[F]): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
HttpRoutes.of[F] {
case GET -> Root / "comics" / authorSlug =>
comics
.getAuthor(Authors.Slug(authorSlug))
.flatMap {
case Some(author) => Ok(author)
case None => NotFound()
}
因此,当存在None
时,它会被转换为404。由于存在多个路由,.flatMap { ... }
会被复制。
问题:如何将其移动到特定于我的项目的单独.orNotFound
辅助函数中?
我的尝试:
为了让事情对我来说更简单(并避免最初在F
上进行参数化(,我尝试在comicsRoutes
:中定义它
def comicsRoutes[F[_]: Sync](comics: Comics[F]): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
def orNotFound[A](opt: Option[A]): ???[A] =
opt match {
case Some(value) => Ok(value)
case None => NotFound()
}
HttpRoutes.of[F] {
case GET -> Root / "comics" / authorSlug =>
comics
.getAuthor(Authors.Slug(authorSlug))
.flatMap(orNotFound)
但这里的???
是什么?它似乎不是Response
或Status
。此外,.flatMap { ... }
是在import dsl._
下制作的,但我想把它移得更远。什么是好地方?它是进入路由文件中,还是放入单独的ExtendedSomething
扩展文件中?(我预计???
和Something
可能有关联,但我有点困惑缺少的类型是什么。(
(同样重要的是,我如何找出这里的???
?我希望类型级别的???
能给我一个"类型化的洞",而VSCode的悬停功能提供了非常零星的文档价值。(
Http4sDsl[F]
为您的操作返回的类型为F[Response[F]]
。
它必须用F
包装,因为您在F
上使用.flatMap
。CCD_ 21使用CCD_ 22进行参数化,因为它将使用F
生成返回给调用者的结果。
要找到这一点,您可以使用IntelliJ,然后通过IDE生成注释(Alt+Enter,然后"将类型注释添加到值定义"(。您也可以:
- 预览隐词以检查从
Statuses
trait导入的Ok
对象是否提供了具有http4sOkSyntax
隐式转换的扩展方法(Ctrl+Alt+Shift+加号,您可以按几次以进一步扩展隐词,Ctrl+Alt+Shift+Minut可再次隐藏隐词( - 按Shift键两次打开查找窗口,再按两次包含非投影符号,即可查找
http4sOkSyntax
- 从那里用Ctrl+B导航
OkOps
到EntityResponseGenerator
类,该类为您提供您(在apply
中(使用的返回F[Resposne[F]]
的功能
因此,如果您想四处移动/提取它们,请注意实例化DSL和扩展方法需要哪些隐含器。
(Mac操作系统(有时使用Cmd而不是Ctrl(和非Mac操作系统之间的快捷方式不同,所以如果您有问题,只需在文档中查看即可(。
多亏了Mateusz,我才知道???
应该是F[Response[F]]
。
为了使这个助手功能完全工作,又出现了两个与类型相关的问题:
-
由于
value: A
是多态的,Http4s需要一个隐式EntityEncoder[F, A]
来序列化任意值。(这与最初的{ case ... }
匹配没有问题,因为类型是具体的,而不是多态的 -
出于某种原因,添加这个隐式注释是不够的。执行
.flatMap(orNotFound)
会使类型推断失败。执行.flatMap(orNotFound[Authors.Slug])
修复了此问题。
(感谢keynmol指出了另外两个。(
有了这三个变化,这导致:
def comicsRoutes[F[_]: Sync](comics: Comics[F]): HttpRoutes[F] = {
val dsl = new Http4sDsl[F] {}
import dsl._
def orNotFound[A](opt: Option[A])(implicit ee: EntityEncoder[F, A]): F[Response[F]] =
opt match {
case Some(value) => Ok(value)
case None => NotFound()
}
HttpRoutes.of[F] {
case GET -> Root / "comics" / authorSlug =>
comics
.getAuthor(Authors.Slug(authorSlug))
.flatMap(orNotFound[Authors.Author])
...