2013-04-21 60 views
17

給定一個無形的HList,其中每個列表元素共享相同的類型構造函數,HList如何排序?測序一個HList

例如:

def some[A](a: A): Option[A] = Some(a) 
def none[A]: Option[A] = None 

val x = some(1) :: some("test") :: some(true) :: HNil 
val y = sequence(x) // should be some(1 :: "test" :: true :: HNil) 

def sequence[L <: HList : *->*[Option]#λ, M <: HList](l: L): Option[M] = 
    ??? 

我試圖執行這樣的序列:

object optionFolder extends Poly2 { 
    implicit def caseOptionValueHList[A, B <: HList] = at[Option[A], Option[B]] { (a, b) => 
    for { aa <- a; bb <- b } yield aa :: bb 
    } 
} 

def sequence[L <: HList : *->*[Option]#λ, M <: HList](l: L): Option[M] = { 
    l.foldRight(some(HNil))(optionFolder) 
} 

但是,這並不編譯:在實施本作無論是

could not find implicit value for parameter folder: shapeless.RightFolder[L,Option[shapeless.HNil.type],SequencingHList.optionFolder.type] 

任何提示像Option這樣的特定示例還是適用於任意應用程序?

回答

17

你是相當接近,你只需要確保你有證據的額外位,它的要求:

def sequence[L <: HList : *->*[Option]#λ, M <: HList](l: L)(implicit 
    folder: RightFolder[L, Option[HNil], optionFolder.type] 
) = l.foldRight(some(HNil: HNil))(optionFolder) 

或者,如果你想要更多的東西一般,並且有一個適用實現這樣的:

trait Applicative[F[_]] { 
    def ap[A, B](fa: => F[A])(f: => F[A => B]): F[B] 
    def point[A](a: => A): F[A] 
    def map[A, B](fa: F[A])(f: A => B): F[B] = ap(fa)(point(f)) 
} 

implicit object optionApplicative extends Applicative[Option] { 
    def ap[A, B](fa: => Option[A])(f: => Option[A => B]) = f.flatMap(fa.map) 
    def point[A](a: => A) = Option(a) 
} 

你可以寫:

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)) 
    } 
} 

def sequence[F[_]: Applicative, L <: HList: *->*[F]#λ, M <: HList](l: L)(implicit 
    folder: RightFolder[L, F[HNil], applicativeFolder.type] 
) = l.foldRight(implicitly[Applicative[F]].point(HNil: HNil))(applicativeFolder) 

現在你可以sequen ce列表等等(假設你有適當的實例)。


更新:請注意,在這兩種情況下,我都省略了sequence的返回類型註釋。如果我們把它放回,編譯器扼流圈:

<console>:18: error: type mismatch; 
found : folder.Out 
required: F[M] 

這是因爲RightFolder例如圍繞它的返回類型爲抽象類型成員攜帶。在這種情況下我們知道它是F[M],但編譯器並不關心我們所知道的。

如果我們希望能更明確一些返回類型,我們可以使用RightFolderAux實例,而不是:

def sequence[F[_]: Applicative, L <: HList: *->*[F]#λ, M <: HList](l: L)(implicit 
    folder: RightFolderAux[L, F[HNil], applicativeFolder.type, F[M]] 
): F[M] = 
    l.foldRight(implicitly[Applicative[F]].point(HNil: HNil))(applicativeFolder) 

注意RightFolderAux有一個額外的類型參數,它表示的返回類型。

+2

謝謝!我試着在提交之前提供隱含的RightFolder,但遇到了上面指出的確切錯誤(需要'F [M]'但是找到'folder.Out')。 RightFolderAux明確表示。 – mpilquist 2013-04-21 15:05:34

+0

我剛剛嘗試了無形狀2.0的序列實現並得到此錯誤:錯誤:(41,36)無法找到參數文件夾的隱式值:shapeless.ops.hlist.RightFolder [L,Option [shapeless.HNil],optionFolder。類型] l.foldRight(Option(HNil:HNil))(optionFolder) ^ – 2014-04-18 22:58:12

+1

@ChanningWalton:在那之前是否有錯誤?你有沒有導入'shapeless.ops.hlist.RightFolder'?它在2.0.0中適用於我。 – 2014-04-19 03:25:13

1

現在你可以使用kittenscats.sequence

import cats.implicits._ 
import cats.sequence._ 
import shapeless._ 

val f1 = (_: String).length 
val f2 = (_: String).reverse 
val f3 = (_: String).toDouble 

val f = (f1 :: f2 :: f3 :: HNil).sequence 
assert(f("42.0") == 4 :: "0.24" :: 42.0 :: HNil) 

測序對功能的例子,但你可以使用任何有一個貓Applicative實例。