简体   繁体   English

leftReduce通用类型的无形HList

[英]leftReduce Shapeless HList of generic types

This is essentially what I want: 这基本上就是我想要的:

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)

So combineHLatest(Foo(1) :: Foo("hello") :: HNil) should yield Foo( (1, "hello") ) 所以combineHLatest(Foo(1) :: Foo("hello") :: HNil)应该产生Foo( (1, "hello") )

The above doesn't compile as it cannot find an implicit LeftReducer but I'm at a loss as to how to implement one. 上面没有编译,因为它找不到隐式的LeftReducer,但我不知道如何实现一个。

It was pretty late last night when I answered this, and while the information in the original answer below is correct, it's not necessarily the most helpful presentation. 昨晚我回答这个问题时已经很晚了,虽然下面原始答案中的信息是正确的,但它不一定是最有用的演示文稿。

There's a good reason you should be at a loss as to how to implement the LeftReducer , since that's not your job. 你不应该对如何实现LeftReducer ,因为那不是你的工作。 The compiler will create any valid instance of the type class you need—you just have to make sure it has all the information it needs. 编译器会创建你需要,你只需要确保它有需要的所有信息类型类别的任何有效的实例。

For example, the following works just fine with your implementation: 例如,以下适用于您的实现:

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

Here the compiler can see the type of the HList you want to reduce, and can create the appropriate LeftReducer instance. 这里编译器可以看到你想要减少的HList的类型,并且可以创建适当的LeftReducer实例。

When you wrap the call to leftReduce up in a method, on the other hand, the compiler doesn't know anything about the list you're calling it on except what you explicitly tell it. 另一方面,当你在一个方法leftReduce的调用leftReduce起来时,编译器对你所调用的列表一无所知,除非你明确告诉它。 In your implementation of combineHLatest , the compiler knows that L is an HList , but that's it—it doesn't have any evidence that it can perform the reduction. 在您对combineHLatest的实现中,编译器知道L是一个HList ,但就是这样 - 它没有任何证据证明它可以执行减少。 Fortunately it's pretty easy to give it this evidence via an implicit parameter (see the original answer below). 幸运的是,通过隐式参数给出这个证据非常容易(参见下面的原始答案)。


I'd originally posted a kind of clunky solution to the flattened-tuple problem here, but the clunkiness was only because of a small typo in my original attempt. 我原本在这里发布了一种对扁平元组问题的笨重解决方案,但笨拙只是因为我最初尝试中的一个小错字。 It's actually possible to write a fairly elegant implementation: 实际上可以编写一个相当优雅的实现:

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)

(My mistake was writing R instead of Foo[R] as the last type parameter on the Aux .) (我的错误是将R而不是Foo[R]Aux上的最后一个类型参数。)


Original answer 原始答案

This will work as expected if you make sure your method has evidence that it can perform the reduction on the input: 如果您确保您的方法有证据表明它可以对输入执行减少,那么这将按预期工作:

import shapeless._, ops.hlist.LeftReducer

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

Note, though, that this approach will just build up a nested tuple if you have more than two arguments, so you may want something more like this: 但是请注意,如果你有两个以上的参数,这种方法只会构建一个嵌套元组,所以你可能想要更像这样的东西:

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)

And then for example: 然后例如:

scala> println(combineHLatest(Foo(1) :: Foo("hello") :: Foo('a) :: HNil))
Foo(1 :: hello :: 'a :: HNil)

If you wanted a (flattened) tuple instead, that'd also be pretty straightforward. 如果你想要一个(扁平的)元组,那也是非常简单的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM