对于理解返回类型

jyo*_*oti 0 for-loop scala list for-comprehension scala-collections

我试图从理解中返回 Future[Vector[String]] ,但我将 Future[Nothing] 作为我的返回类型。如何将 Future[Nothing] 返回类型转换为 Future[Vector[String]]?这是代码片段:

def findProjectId(projectName: String, userId: String): Future[Nothing] = {
    for {
      projectIds <- dBService.fetchProjectIdByProjectName(projectName) // projectIds is Vector[String]
      pid <- projectIds
      projectId <- dBService.filterProjectId(pid, userId) // ProjectId is Vector[String]
      if projectId.nonEmpty
    } yield {
      projectId map {
        projectid =>
          dBService.fetchRoleId map { // fetchRoleId returns Future[Vector[String]]
            case Vector(rid) => dBService.fetchCollaborator(projectid, rid) map { // fetchCollaborator returns Future[Vector[String]]
              listOfcollabs =>
                if (listOfcollabs.nonEmpty) {
                  listOfcollabs ++ Vector(userId)
                }
                else {
                  Vector(userId)
                }
            }
          }
      }
    }
  }
Run Code Online (Sandbox Code Playgroud)

dbService 方法的签名是:

  def checkCollaboratorOrAdmin(userId: String, projectId: String, roleId: String): Future[Vector[String]] = {
    dbComponent.db.run(checkAdminOrCollab(roleId))
  }

  def fetchRoleId: Future[Vector[String]] = {
    dbComponent.db.run(fetchRole)
  }

  def fetchCollaborator(roleId: String, projectId: String): Future[Vector[String]] = {
    dbComponent.db.run(fetchCollaborators(roleId, projectId))
  }

  def fetchProjectIdByProjectName(projectName: String) = {
    dbComponent.db.run(fetchProjectId(projectName))
  }

  def filterProjectId(projectId: String, userId: String) = {
    dbComponent.db.run(filterProjectIdByUserId(projectId, userId))
  }
Run Code Online (Sandbox Code Playgroud)

这些方法依次调用:

  def fetchRoleId(userId: String, projectId: String): SqlStreamingAction[Vector[String], String, Effect] = {
    sql"""select distinct(role_id) from project_user_role where user_id=$userId and project_id=$projectId""".as[String]
  }

  def checkAdminOrCollab(roleId: String): SqlStreamingAction[Vector[String], String, Effect] = {
    sql"""select role_id from roles where role_id=$roleId and role_name="collaborator" """.as[String]
  }

  def fetchRole(): SqlStreamingAction[Vector[String], String, Effect] = {
    sql"""select role_id from roles where role_name="collaborator"""".as[String]
  }

  def fetchCollaborators(roleId: String, projectId: String): SqlStreamingAction[Vector[String], String, Effect] = {
    sql"""select user_id from project_user_role where roleId=$roleId and project_id=$projectId""".as[String]
  }

  def fetchProjectId(projectName: String): SqlStreamingAction[Vector[String], String, Effect] = {
    sql"""select project_id from projects where project_name=$projectName""".as[String]
  }

  def filterProjectIdByUserId(projectId: String, userId: String): SqlStreamingAction[Vector[String], String, Effect] = {
    sql"""select project_id from project_user_role where project_id=$projectId and user_id=$userId""".as[String]
  }
Run Code Online (Sandbox Code Playgroud)

Tim*_*Tim 5

我猜这Future[Nothing]来自 IntelliJ 提示,而不是编译器本身。编译器给出了几个错误,第一个来自这一行:

pid <- projectIds
Run Code Online (Sandbox Code Playgroud)

这是我得到的错误:

test.scala:47:13: 类型不匹配;

[错误] 发现:scala.collection.immutable.Vector[Int]

[错误] 需要:scala.concurrent.Future[?]

问题是,该for表达式试图建立一个类型的值Future[_]用使用map,flatMap并filter调用。(afor的集合类型是 中第一个表达式的集合类型for)。flatMap在Future需要Future和曲折Future[Future[_]]成Future[_]。但是你给它一个Vector不受支持的。

我也不确定更广泛的逻辑,因为您有两个嵌套的Vectors(projectIds 和 listOfCollabs),但没有将其展平为单个向量的机制。

您可能想要查看 usingFuture.traverse或Future.sequence将 的列表Future转换为Future[List].

将其分解为一些命名函数以使代码更易于理解并提供更好的隔离问题的机会也是有意义的。

更新

此代码将调用适当的函数并返回结果。返回类型是Future[Vector[Vector[Vector[String]]]]因为每次dBService调用都会返回,Future[Vector[String]]因此您会得到嵌套的Vectors。从如何将其展平为您想要的结果的问题中不清楚,但它应该很简单。(最后一次调用的结果被case语句弄平了,这就是为什么有 4 个dBService调用但只有 3 个嵌套的原因Vectors)

def findProjectId(projectName: String, userId: String): Future[Vector[Vector[Vector[String]]]] = {
  dBService.fetchProjectIdByProjectName(projectName).flatMap { projectIds =>
    Future.traverse(projectIds) { pid =>
      dBService.filterProjectId(pid, userId).flatMap { projectId =>
        Future.traverse(projectId) { projectid =>
          dBService.fetchRoleId.flatMap { // fetchRoleId returns Future[Vector[String]]
            case Vector(rid) =>
              dBService.fetchCollaborator(projectid, rid) map { // fetchCollaborator returns Future[Vector[String]]
                _ ++ Vector(userId)
              }
          }
        }
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

可能您可以只采用其中一些Vectors可以简化代码的第一个元素。

Future.traverse(collection)(f)等效于Future.sequence(collection.map(f))但提供更好的布局并且可能更有效。