【问题标题】:Scala: composing queries inside for comprehension giving errorsScala:在内部编写查询以理解给出错误
【发布时间】:2017-10-03 18:19:05
【问题描述】:

我正在尝试正确执行此查询,但出现错误。首先,searchUser 返回一个匹配的 UserEntries 序列,其中包含用户的唯一 ID,对于每个 userId,第二个查询从另一个表中获取一些其他用户信息 + 地址。

代码:

def searchUsers(pattern: String) = auth.SecuredAction.async {
  implicit request =>
    usersService.searchUser(pattern) flatMap { usrList =>
      for {
        u <- usrList
        ui <- usersService.getUsersWithAddress(u.id)
      } yield {
        Ok(views.html.UserList(ui))
      }
    }
}

所用 API 的签名:

def searchUser(pattern: String): Future[Seq[UserEntry]] = ...    
def getUsersWithAddress(userId: Long): Future[Seq[(UserProfile, Seq[String])]] = ...

错误:

[error] modules/www/app/controllers/Dashboard.scala:68: type mismatch;
[error]  found   : scala.concurrent.Future[play.api.mvc.Result]
[error]  required: scala.collection.GenTraversableOnce[?]
[error]           ui <- usersService.getUsersWithAddress(u.id)
[error]              ^
[error] one error found
[error] (www/compile:compileIncremental) Compilation failed

如果我注释掉“u

【问题讨论】:

    标签: scala


    【解决方案1】:

    当您在 for 理解中使用多个生成器时,monad 必须是同一类型。例如。你不能:

    scala> for{ x <- Some("hi"); y <- List(1,2,3) } yield (x,y)
    <console>:11: error: type mismatch;
    found   : List[(String, Int)]
    required: Option[?]
              for{ x <- Some("hi"); y <- List(1,2,3) } yield (x,y)
                                      ^
    

    您可以做的是转换一个或另一个以匹配正确的类型。对于上面的例子,那就是:

    scala> for{ x <- Some("hi").toSeq; y <- List(1,2,3) } yield (x,y)
    res2: Seq[(String, Int)] = List((hi,1), (hi,2), (hi,3))
    

    在您的特定情况下,您的一个生成器是 GenTraversableOnce,另一个是 Future。您可能可以使用 Future.successful(theList) 来获得两个期货。例如,请参阅此处的答案:

    Unable to use for comprehension to map over List within Future

    【讨论】:

    • 如果我用 Future.successful() 包装 u
    • 根据您的答案跟踪了一系列答案,最终得到了解决方案。由于我似乎无法在评论中放置一段格式化的代码,因此我将其添加为我的答案——但是接受你的,给你应有的信用!
    【解决方案2】:

    根据@Brian 的回答得出了一个解决方案.. 以下工作(无法输入格式化程序代码块作为注释 - 所以添加为答案):

      usersService.searchUser(pattern) flatMap { usrList =>
        val q = for {
          u <- usrList
        } yield (usersService.getUserWithAddress(u.id))
        val r = Future.sequence(q)
        r map { ps =>
          Ok(views.html.UserList(ps))
        } 
      }
    

    为了理解,积累了 Futures,然后将序列展平,然后映射。希望这就是它的完成方式!

    注意:我还必须将 getUserWithAddress 的签名更改为 X 而不是 Seq[X]

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-08
      • 2015-08-26
      • 2019-05-30
      • 2020-04-07
      • 1970-01-01
      • 2019-08-27
      • 1970-01-01
      • 2016-12-08
      相关资源
      最近更新 更多