2014-05-04 17 views
6

有沒有簡單的方法來轉換類型的元組(Future [A],Future [B],Future [C],...,Future [N])到未來[(A,B,C,...,N)]?這假定元組中的元素數量未定。使用shapeless將元組的Future轉換爲Future元組通過HList

我已經嘗試將元組轉換爲HList,並嘗試類似於foldLeft的技巧,並在Future.sequence中進行了理解,但沒有處理傳入摺疊的類型。嘗試與遞歸函數相同。但是由於缺少HList.head,HList.tail,此代碼仍然無法編譯。代碼如下所示:

def sequence[L <: HList](in: L)(implicit ihc: IsHCons[L]) = { 

    val list = for (i <- in.head; j <- in.tail.head) yield HList(i, j) 

    @tailrec 
    def sequence2(fList: Future[HList], listF: HList): Future[HList] = { 
    if (listF == HNil) fList 
    else { 
     val nextFList = for (l <- fList; e <- listF.head.asInstanceOf[Future[_]]) yield l :+ e 
     sequence2(nextFList, listF.tail) 
    } 
    } 

    sequence2(list, in.tail.tail) 
} 

此代碼應返回的未來[HList]然後我們可以用tupled函數映射回元組。理想情況下,我們需要檢查元組中少於3個元素的事實。但讓我們假設這個練習的輸入是一個3或更大的HList。

我在無形狀1.2.4,由於其他依賴關係無法移動。

在此先感謝!

回答

10

不知道這是否算作「簡單」,但Dan Lien和我討論如何序列Option小號just the other day元組,並my solution對於這種情況下,可以被直接用於爲Future(工作注意,我使用Futurescalaz-contrib的單子實例;如果你在Scalaz 7.1,這是沒有必要的):

import scala.concurrent.{ ExecutionContext, Future } 
import scalaz._, Scalaz._, contrib.std.scalaFuture._ 
import shapeless._, ops.hlist.{ RightFolder, Tupler } 

// Might as well stay generic in `F` for this part. 
object applicativeFolder extends Poly2 { 
    implicit def caseApplicative[A, B <: HList, F[_]](implicit 
    app: Applicative[F] 
) = at[F[A], F[B]] { 
    (a, b) => app.ap(a)(app.map(b)(bb => (_: A) :: bb)) 
    } 
} 

// It should be possible to make this part generic in `F` as well, 
// but type inference makes it tricky, so we specialize to `Future`. 
def sequence[T, EL <: HList, L <: HList, OL <: HList, OT](t: T)(implicit 
    executor: ExecutionContext, 
    gen: Generic.Aux[T, EL], 
    eq: EL =:= L, 
    folder: RightFolder.Aux[L, Future[HNil], applicativeFolder.type, Future[OL]], 
    tupler: Tupler.Aux[OL, OT] 
): Future[OT] = 
    eq(gen.to(t)).foldRight(Future.successful(HNil: HNil))(applicativeFolder).map(
    tupler(_) 
) 

哦,只注意到你在1.2.4。必要的修改基本上是機械像下面應該工作:

// It should be possible to make this part generic in `F` as well, 
// but type inference makes it tricky, so we specialize to `Future`. 
def sequence[T, L <: HList, OL <: HList, OT](t: T)(implicit 
    executor: ExecutionContext, 
    hlister: HListerAux[T, L], 
    folder: RightFolderAux[L, Future[HNil], applicativeFolder.type, Future[OL]], 
    tupler: TuplerAux[OL, OT] 
): Future[OT] = 
    t.hlisted.foldRight(Future.successful(HNil: HNil))(applicativeFolder).map(
    tupler(_) 
) 

它的工作原理是這樣的:

scala> import scala.concurrent.ExecutionContext.Implicits.global 
import scala.concurrent.ExecutionContext.Implicits.global 

scala> val result = sequence((Future(1), Future('a))) 
result: scala.concurrent.Future[(Int, Symbol)] = ... 

scala> result.foreach(println) 
(1,'a) 

注意,存在shapeless-contrib一個sequence實現,但由於種種原因(包括類型推理)在這種情況下很難使用。

+0

'caseApplicative'的類型是'F [A :: B]'? – pedrofurla

+0

是的,但在'Case'包裝中。 –

+0

試一試。也許我不完整。 hlisted不可用於類型T.爲了允許t的隱式轉換,我必須使用def序列[T <:Product,L <:HList ...不確定它甚至是正確的。 – akara

相關問題