2

我有一个如下所示的 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)

但是???这里有什么?似乎不是ResponseStatus。此外,它.flatMap { ... }是在 下制作的import dsl._,但我想把它移到更远的地方。什么是好地方?它是进入路由文件,还是我把它放在一个单独的ExtendedSomething扩展文件中?(我希望这可能是相关的???Something但我对缺少的类型有点困惑。)

(同样重要的是,我如何找出???这里有什么?我希望???在类型级别可以给我一个“类型漏洞”,而 VSCode 的悬停功能提供了非常零星的文档价值。)

4

2 回答 2

3

Http4sDsl[F]为您的操作返回的类型是F[Response[F]].

它必须被包裹,F因为您正在使用.flatMapon FResponse被参数化,F因为它会产生使用 . 返回给调用者的结果F

要找出这一点,您可以使用 IntelliJ,然后通过 IDE 生成注释(Alt+Enter,然后“将类型注释添加到值定义”)。你也可以:

  • 预览implicits以检查OkStatusestrait导入的对象是否提供了http4sOkSyntax隐式转换的扩展方法(Ctrl+Alt+Shift+加号,您可以按几次以进一步扩展implicits,然后按Ctrl+Alt+Shift+Minut再次隐藏它们)
  • http4sOkSyntax按两次 Shift 打开查找窗口,然后再次按两次以包含非项目符号,
  • 从那里使用 Ctrl+B 导航OkOpsEntityResponseGenerator为您提供您使用(在apply)返回的功能的类F[Resposne[F]]

因此,如果您想移动/提取它们,请注意实例化 DSL 和扩展方法所需的隐式。

(Mac OS(有时使用 Cmd 而不是 Ctrl)和非 Mac OS 系统之间的快捷方式不同,因此如果您有问题,只需在文档中检查它们)。

于 2020-11-26T12:12:08.097 回答
0

感谢 Mateusz,我了解到???应该是F[Response[F]].

为了让这个辅助函数充分发挥作用,又出现了两个与类型相关的问题:

  1. 由于value: A是多态的,Http4s 需要一个隐式 EntityEncoder[F, A]来序列化任意值。(这对原始{ case ... }匹配不是问题,因为类型是具体的而不是多态的。

  2. 由于某种原因,添加这个隐式注释是不够的。执行.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])
      ...
于 2020-11-26T21:01:48.857 回答