leftReduce 泛型类型的无形状 HList [英] leftReduce Shapeless HList of generic types

查看:37
本文介绍了leftReduce 泛型类型的无形状 HList的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这基本上就是我想要的:

case class Foo[T](x: T)对象组合扩展 Poly {隐式 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)

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

上述内容无法编译,因为它找不到隐式 LeftReducer,但我不知道如何实现.

解决方案

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

对于如何实现 LeftReducer,您有充分的理由感到茫然,因为那不是您的工作.编译器将创建您需要的类型类的任何有效实例——您只需确保它具有需要的所有信息.

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

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

这里编译器可以看到你想要减少的HList的类型,并且可以创建合适的LeftReducer实例.

另一方面,当你把对 leftReduce 的调用封装在一个方法中时,编译器除了你明确告诉它的以外,对你调用它的列表一无所知.在您的 combineHLatest 实现中,编译器知道 L 是一个 HList,但仅此而已 - 它没有任何证据表明它可以执行减少.幸运的是,通过隐式参数为其提供此证据非常容易(请参阅下面的原始答案).

<小时>

我最初在这里发布了一种针对扁平元组问题的笨拙解决方案,但笨拙只是因为我最初尝试中的一个小错字.实际上可以编写一个相当优雅的实现:

def combineHlatest[L <: HList, R <: HList](l: L)(隐式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.LeftReducerdef combineHlatest[L <: HList](l: L)(隐式 r: LeftReducer[L, combine.type]) =l.reduceLeft(组合)

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

object combine extends Poly {隐式 def caseFoo[A, B <: HList] = use((f1: Foo[A], f2: Foo[B]) =>富(f1.x :: f2.x))}def combineHlatest[L <: HList](l: L)(隐式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 :: 你好 :: 'a :: HNil)

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

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

The above doesn't compile as it cannot find an implicit LeftReducer but I'm at a loss as to how to implement one.

解决方案

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. 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.

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


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.

这篇关于leftReduce 泛型类型的无形状 HList的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆