2013-04-21 59 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实例。