List[OptionT[Future, Int]] в OptionT[Future, List[A]]

Я создаю List из Int, используя асинхронное вычисление для извлечения элемента:

(1 to n).map(anAsyncThingy).toList

где anAsyncThingy возвращает OptionT[Future, Int]

Следовательно, результат имеет тип List[OptionT[Future, Int]]

То, что я хотел бы получить сейчас, это OptionT[Future, List[A]]

Вот моя лучшая попытка (я добавлю несколько заглушек, чтобы она могла работать в REPL)

import scalaz._; import Scalaz._
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global

def anAsyncThingy(x: Int): OptionT[Future, Int] = x.point[Future].liftM[OptionT]

val res = OptionT {
  Future.sequence {
    (1 to 3).map(anAsyncThingy(_).run).toList
  }.map(_.sequence)
}
res.map(println) // List(1, 2, 3)

Вышеупомянутое работает, как и ожидалось, но я чувствую, что есть много возможностей для улучшения, используя правильные конструкции scalaz, вместо того, чтобы прыгать от монадных трансформеров.

Как я могу добиться того же результата более простым способом?


person Gabriele Petronella    schedule 28.01.2015    source источник


Ответы (1)


Я нашел ответ сам, после некоторых экспериментов:

val res = (1 to 3).map(anAsyncThingy).toList.sequenceU
res.map(println) // List(1, 2, 3)

Ура для скалаза!

Кстати, вместо sequence нужно sequenceU, потому что scala недостаточно умна, чтобы понять, когда у вас есть

OptionT[M, A]

если вы исправите параметр типа (например, от M до Future)

OptionT[Future, A]

он имеет форму M[_]

Компилятор продолжает полагать, что он имеет форму M[_, _], если только он не заполнен (с лямбдой неприятного типа)

Вот тут-то и вступает в действие sequenceU от scalaz, который решает эту проблему с помощью Unapply. Подробнее на эту тему здесь.

Обновлять

Согласно комментарию phadej, sequence ∘ map ≡ traverse, так что это можно сделать еще более кратким с помощью traverseU:

(1 to 3).toList.traverseU(anAsyncThingy)

Та же идея sequence против sequenceU применима и к traverse против traverseU, конечно.

person Gabriele Petronella    schedule 28.01.2015
comment
В качестве примечания: sequence ∘ map ≡ traverse, так что лучше напишу: (1 to 3).toList traverseU anAsyncThingy - person phadej; 28.01.2015