leftReduce通用类型的无形HList

Cha*_*ton 7 scala shapeless

这基本上就是我想要的:

case class Foo[T](x: T)

object combine extends Poly {
  implicit def caseFoo[A, B] = use((f1: Foo[A], f2: Foo[B]) => Foo((f1.x, f2.x)))
}

def combineHLatest[L <: HList](l: L) = l.reduceLeft(combine)
Run Code Online (Sandbox Code Playgroud)

所以combineHLatest(Foo(1) :: Foo("hello") :: HNil)应该屈服Foo( (1, "hello") )

上面没有编译,因为它找不到隐式的LeftReducer,但我不知道如何实现一个.

Tra*_*own 6

昨晚我回答这个问题时已经很晚了,虽然下面原始答案中的信息是正确的,但它不一定是最有用的演示文稿.

你不应该对如何实施这个问题感到茫然LeftReducer,因为那不是你的工作.编译器会创建你需要,你只需要确保它拥有所有的信息的类型类的任何有效的情况下的需求.

例如,以下适用于您的实现:

scala> (Foo(1) :: Foo("hello") :: HNil).reduceLeft(combine)
res0: Foo[(Int, String)] = Foo((1,hello))
Run Code Online (Sandbox Code Playgroud)

在这里,编译器可以看到HList您要减少的类型,并可以创建适当的LeftReducer实例.

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)
Run Code Online (Sandbox Code Playgroud)

(我的错误是写R而不是Foo[R]作为最后一个类型参数Aux.)


原始答案

如果您确保您的方法有证据表明它可以对输入执行减少,那么这将按预期工作:

import shapeless._, ops.hlist.LeftReducer

def combineHLatest[L <: HList](l: L)(implicit r: LeftReducer[L, combine.type]) =
  l.reduceLeft(combine)
Run Code Online (Sandbox Code Playgroud)

但是请注意,如果你有两个以上的参数,这种方法只会构建一个嵌套的元组,所以你可能想要更像这样的东西:

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)
Run Code Online (Sandbox Code Playgroud)

然后例如:

scala> println(combineHLatest(Foo(1) :: Foo("hello") :: Foo('a) :: HNil))
Foo(1 :: hello :: 'a :: HNil)
Run Code Online (Sandbox Code Playgroud)

如果你想要一个(扁平的)元组,那也是非常简单的.