Составление фьючерсов с помощью For Comprehension

У меня есть приложение Play Framework, использующее ReactiveMongo с MongoDB, и у меня есть следующий код:

def categories(id: String): Future[Vector[Category]] = {...}
....
val categoriesFuture = categories(id)
for {
  categories: Vector[Category] <- categoriesFuture
  categoryIdsWithoutPerson: Vector[BSONObjectID] <- findCategoryIdsWithoutPerson(categories.map(_.id), personId) //Returns Future[Vector[BSONObjectID]]
  categoriesWithoutPerson: Vector[Category] <- categories.filter(category => categoryIdsWithoutPerson.contains(category.id)) //Play cites the error here
} yield categoryIdsWithoutPerson

Чтобы объяснить этот код, я получаю Vector из Categories, завернутых в Future, потому что так работает ReactiveMongo. В понимании for я использую этот Vector, чтобы затем получить список идентификаторов из базы данных. Наконец, я использую вызов filter, чтобы сохранить только те категории, идентификаторы которых можно найти в этом списке идентификаторов.

Все кажется довольно простым. Проблема в том, что Play выдает следующую ошибку компиляции в последней строке понимания for:

pattern type is incompatible with expected type;
 found   : Vector[com.myapp.Category]
 required: com.myapp.Category

Я не уверен, почему требуемый тип является единственным экземпляром Category.

Я мог бы использовать некоторое понимание того, что я делаю неправильно, и / или есть ли более простой или более идиоматический способ выполнить это.


person Vidya    schedule 23.11.2014    source источник
comment
не могли бы вы опубликовать подпись метода «категории»? а в какой именно строке обнаружена ошибка?   -  person ale64bit    schedule 24.11.2014
comment
Уточнил по просьбе. Спасибо.   -  person Vidya    schedule 24.11.2014
comment
просто любопытно, почему в результате получается категория IdsWithoutPerson? зачем последняя строчка for-comprehension?   -  person ale64bit    schedule 24.11.2014


Ответы (1)


Похоже, вы пытаетесь составить Futures с Vector. Для понимания в scala все должны быть одного и того же более высокого типа, который в вашем случае Future. Когда вы разворачиваете «сахар» для понимания, он просто вызывает flatMap на все.

for {
  categories <- categoriesFuture
  // I'm not sure what the return type is here, but I'm guessing it's a future as well
  categoryIdsWithoutPerson <- findCategoryIdsWithoutPerson(categories.map(_.id), personId)
  // Here we use = to get around need to flatMap
  categoriesWithoutPerson = categories.filter(category => categoryIdsWithoutPerson.contains(category.id))
} yield categoryIdsWithoutPerson

Ваш код очищен от сахара:

categoriesFuture.flatMap(categories => 
  findCategoryIdsWithoutPerson(categories.map(_.id), personId).
    flatMap(categoryIdsWithoutPerson => 
       categories.filter(category => categoryIdsWithoutPerson.contains(category.id)).
          map(_ => categoryIdsWithoutPerson))
person Noah    schedule 23.11.2014