昨晚我回答这个问题时已经很晚了,虽然下面原始答案中的信息是正确的,但它不一定是最有用的演示文稿。
您有充分的理由不知道如何实施LeftReducer
,因为那不是您的工作。编译器将创建您需要的类型类的任何有效实例——您只需确保它具有所需的所有信息。
例如,以下内容适用于您的实现:
scala> (Foo(1) :: Foo("hello") :: HNil).reduceLeft(combine)
res0: Foo[(Int, String)] = Foo((1,hello))
在这里编译器可以看到HList
你想要减少的类型,并且可以创建相应的LeftReducer
实例。
另一方面,当您将对 up 的调用包装leftReduce
在方法中时,编译器对您调用它的列表一无所知,除非您明确告诉它。在您的 实现中combineHLatest
,编译器知道这L
是一个HList
,但仅此而已 - 它没有任何证据表明它可以执行归约。幸运的是,很容易通过隐式参数给它这个证据(见下面的原始答案)。
我最初在这里为扁平元组问题发布了一种笨拙的解决方案,但笨拙只是因为我最初尝试中的一个小错字。实际上可以编写一个相当优雅的实现:
def combineHLatest[L <: HList, R <: HList](l: L)(implicit
r: RightFolder.Aux[L, Foo[HNil], combine.type, Foo[R]],
t: Tupler[R]
) = Foo(l.foldRight(Foo(HNil: HNil))(combine).x.tupled)
(我的错误是写R
而不是Foo[R]
作为最后一个类型参数Aux
。)
原始答案
如果您确保您的方法有证据表明它可以对输入执行减少,这将按预期工作:
import shapeless._, ops.hlist.LeftReducer
def combineHLatest[L <: HList](l: L)(implicit r: LeftReducer[L, combine.type]) =
l.reduceLeft(combine)
但是请注意,如果您有两个以上的参数,这种方法只会构建一个嵌套元组,所以您可能想要更像这样的东西:
object combine extends Poly {
implicit def caseFoo[A, B <: HList] = use(
(f1: Foo[A], f2: Foo[B]) => Foo(f1.x :: f2.x)
)
}
def combineHLatest[L <: HList](l: L)(implicit
r: RightFolder[L, Foo[HNil], combine.type]
) = l.foldRight(Foo(HNil: HNil))(combine)
然后例如:
scala> println(combineHLatest(Foo(1) :: Foo("hello") :: Foo('a) :: HNil))
Foo(1 :: hello :: 'a :: HNil)
如果你想要一个(扁平的)元组,那也很简单。