Budujem budovu List
z Int
použitie asynchrónneho výpočtu na načítanie prvku:
(1 to n).map(anAsyncThingy).toList
kde anAsyncThingy
výnos OptionT[Future, Int]
Výsledkom je teda typ List[OptionT[Future, Int]]
To, čo by som teraz rád získal, je OptionT[Future, List[A]]
Tu je môj najlepší pokus (pridám pár útržkov, aby mohol bežať v 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)
Horeuvedené práce fungujú podľa očakávania, ale cítim, že existuje veľa priestoru na zlepšenie pomocou vhodných skeletových konštruktov, namiesto preskočenia a vyskočenia z monadových transfomérov.
Ako môžem dosiahnuť ten istý výsledok jednoduchším spôsobom?
odpovede:
4 pre odpoveď č. 1Odpoveď som našiel sám po niekoľkých pokusoch:
val res = (1 to 3).map(anAsyncThingy).toList.sequenceU
res.map(println) // List(1, 2, 3)
Yay for scalaz!
mimochodom sequenceU
je potrebný namiesto sequence
pretože Scala nie je natoľko šikovný, aby zistil, kedy máš
OptionT[M, A]
ak opravíte parameter type (M
na Future
napríklad)
OptionT[Future, A]
má tvar M[_]
Kompilátor stále verí, že má M[_, _]
tvar, pokiaľ nie je spoonfed (s škaredým typom lambda)
Tu je "scalaz" sequenceU
kopy a rieši tento problém pomocou Unapply
, Viac na túto tému tu.
aktualizovať
Podľa komentára Phadeja sequence ∘ map ≡ traverse
, aby sa to dalo ešte skrátiť traverseU
:
(1 to 3).toList.traverseU(anAsyncThingy)
Rovnaká myšlienka sequence
proti sequenceU
týka sa traverse
proti traverseU
, samozrejme.