对于理解返回类型

时间:2018-08-05 06:18:20

标签: scala list for-loop scala-collections for-comprehension

我正在尝试从中返回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)
                }
            }
          }
      }
    }
  }

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))
  }

这些方法依次调用:

  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]
  }

1 个答案:

答案 0 :(得分:4)

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

pid <- projectIds

这是我得到的错误:

  

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

     

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

     

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

问题是for表达式试图使用Future[_]mapflatMap调用来构建类型为filter的值。 (for的集合类型是for中第一个表达式的集合类型)。 flatMap上的FutureFuture并将Future[Future[_]]变成Future[_]。但是,您给它提供了Vector不受支持。

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

您可能希望使用Future.traverseFuture.sequenceFuture的列表转换为Future[List]

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

更新

此代码将调用适当的函数并返回结果。返回类型为Future[Vector[Vector[Vector[String]]]],因为每个dBService调用都会返回Future[Vector[String]],因此您会嵌套Vector。从这个问题尚不清楚如何将其展平为所需的结果,但是它应该很简单。 (最后一次调用的结果被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)
              }
          }
        }
      }
    }
  }
}

也许您可以只使用其中一些Vectors的第一个元素来简化代码。

Future.traverse(collection)(f)Future.sequence(collection.map(f))等效,但是布局更好,效率更高。