无形HList上的参数化折叠 [英] Parameterized folding on a shapeless HList

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

问题描述

我正在尝试实现一种在调用方提供的HList上进行参数化折叠的方法.HList可以具有任意数量的相同类型的元素(> 0).

I am trying to implement a method that does parameterized folding on a HList provided by the caller. The HList can have any number of elements (> 0) of the same type.

val list = "a" :: "b" :: "c" :: HNil

def process[L <: HList](mul: Int, l: L) = {
  object combine extends Poly2 {
    implicit def work = at[String, (Int, L)] {
      case (a, (b, acc)) => (b, (a * b) :: acc)
    }
  }
  l.foldRight((mul, HNil))(combine)._2
}

process(3, list)    //  expecting to get aaa :: bbb :: ccc :: HNil

我得到的是关于缺少隐式的错误:找不到参数文件夹的隐式值:shapeless.ops.hlist.RightFolder [L,(Int,shapeless.HNil.type),combine.type]".从此答案中可以明显看出,编译器希望看到可以折叠的证据HList.

What I get is error about missing implicit: "could not find implicit value for parameter folder: shapeless.ops.hlist.RightFolder[L,(Int, shapeless.HNil.type),combine.type]". From this answer it is clear that compiler wants to see evidence that it can fold the HList.

但是,我无法将RightFolder作为隐式参数传递,因为方法外部不知道Poly2类型.即使有可能,隐式参数也只会在调用堆栈中进一步传播.实际上,我甚至不希望调用者知道该方法是否执行折叠,映射,缩小或其他任何操作.它需要提供的所有证据就是HList是正确的HList类型.我认为问题出在[L< ;: HList]中,该问题不够具体,但我不确定如何正确设置.

However I cannot pass a RightFolder as an implicit parameter because Poly2 type is not known outside the method. And even if it were possible, the implicit parameter would only propagate further up the call stack. In fact I don't want the caller to even know whether the method performs folding, mapping, reduction or anything else. All it needs to provide is evidence that the HList is the right kind of HList. I assume the problem is in [L <: HList] which is not specific enough but I am not sure how to make it right.

以下代码按预期工作,但显然不会将折叠逻辑封装在方法中:

The following code works as expected but it obviously does not encapsulate folding logic in a method:

val list = "a" :: "b" :: "c" :: HNil

object combineS extends Poly2 {
  implicit def work[L <: HList] = at[String, (Int, L)] {
    case (a, (b, acc)) => (b, (a * b) :: acc)
  }
}

list.foldRight((3, HNil))(combineS)._2

推荐答案

最简单的方法是提取 combine (添加类型参数 L )并将必要的隐式参数添加到过程.

The easiest is to extract combine (adding type parameter L) and add necessary implicit parameter to process.

object combine extends Poly2 {
  implicit def work[L <: HList] = at[String, (Int, L)] {
    case (a, (b, acc)) => (b, (a * b) :: acc)
  }
}

def process[L <: HList](mul: Int, l: L)(implicit rightFolder: RightFolder.Aux[L, (Int, HNil.type), combine.type, _ <: (_,_)]) = {
  l.foldRight((mul, HNil))(combine)._2
}


即使有可能,隐式参数也只会在调用堆栈中进一步传播.实际上,我甚至不希望调用者知道该方法是否执行折叠,映射,缩小或其他任何操作.

And even if it were possible, the implicit parameter would only propagate further up the call stack. In fact I don't want the caller to even know whether the method performs folding, mapping, reduction or anything else.

使用类型级编程,您可以将逻辑封装在类型类中,而不是方法中.因此,您可以引入类型类

With type-level programming you encapsulate your logic in a type class rather than method. So you can introduce a type class

trait Process[L <: HList] {
  type Out <: HList
  def apply(mul: Int, l: L): Out
}
object Process {
  type Aux[L <: HList, Out0 <: HList] = Process[L] { type Out = Out0 }

  object combine extends Poly2 {
    implicit def work[L <: HList] = at[String, (Int, L)] {
      case (a, (b, acc)) => (b, (a * b) :: acc)
    }
  }

  implicit def mkProcess[L <: HList, Res, A, L1 <: HList](implicit
    rightFolder: RightFolder.Aux[L, (Int, HNil.type), combine.type, Res],
    ev: Res <:< (A, L1)
  ): Aux[L, L1] = new Process[L] {
    override type Out = L1
    override def apply(mul: Int, l: L): Out = l.foldRight((mul, HNil))(combine)._2
  }
}

def process[L <: HList](mul: Int, l: L)(implicit p: Process[L]): p.Out = p(mul, l)

这篇关于无形HList上的参数化折叠的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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