HList foldLeft,元组为零

问题描述

我正在尝试使用类型为foldLeft的累加器在HList上使用(HL,Int),其中HL是HList。下面的程序无法编译。但是,如果我切换到类型更简单的HL类型的累加器(只需将注释行替换为上面的注释行),它将编译并起作用。

一个元组中包装一个HList会打破leftFolder的隐式分辨率。我想念什么?

package foo.bar

import shapeless.{:+:,::,CNil,coproduct,Generic,HList,HNil,Lazy,poly2}
import shapeless.ops.hlist.{LeftFolder,Reverse}

object StackOverflow extends App {

  trait MyTypeclass[T] {
    def doSomething(t: T): (T,Int)
  }

  implicit lazy val stringInstance: MyTypeclass[String] = (t: String) => (t,0)
  implicit val hnilInstance: MyTypeclass[HNil] = (t: HNil) => (t,0)
  implicit def hlistInstance[H,T <: HList](
    implicit
    head: Lazy[MyTypeclass[H]],tail: MyTypeclass[T]
  ): MyTypeclass[H :: T] =
    (ht: H :: T) =>
      ht match {
        case h :: t =>
          val (hres,hint) = head.value.doSomething(h)
          val (tres,tint) = tail.doSomething(t)
          (hres :: tres,hint + tint)
    }
  implicit val cnilInstance: MyTypeclass[CNil] = (t: CNil) => ???
  implicit def coproductInstance[L,R <: coproduct](
    implicit
    head: Lazy[MyTypeclass[L]],tail: MyTypeclass[R]
  ): MyTypeclass[L :+: R] = (lr: L :+: R) => ???

  object leftFolder extends poly2 {
    implicit def caseAtSimple[F,HL <: HList]: Case.Aux[HL,F,F :: HL] =
      at {
        case (acc,f) => f :: acc
      }
    implicit def caseAtComplex[F,HL <: HList]: Case.Aux[(HL,Int),(F :: HL,Int)] =
      at {
        case ((acc,i),f) => (f :: acc,i)
      }
  }

  implicit def genericInstance[T,HL <: HList,LL <: HList](
    implicit
    gen: Generic.Aux[T,HL],myTypeclass: Lazy[MyTypeclass[HL]],//    folder: LeftFolder.Aux[HL,leftFolder.type,LL],folder: LeftFolder.Aux[HL,(HNil,(LL,Int)],reverse: Reverse.Aux[LL,HL]
  ): MyTypeclass[T] = (t: T) => {
    val generic = gen.to(t)
    val (transformed,idx) = myTypeclass.value.doSomething(generic)
//    val ll = transformed.foldLeft(HNil: HNil)(leftFolder)
    val (ll,_) = transformed.foldLeft((HNil: HNil,0))(leftFolder)
    val reversed = reverse(ll)
    (gen.from(reversed),idx)
  }

  def doSomething[T](t: T)(implicit myTypeclass: MyTypeclass[T]): T = myTypeclass.doSomething(t)._1

  case class Foo(
    str1: String,str2: String
  )

  val original = Foo("Hello World!","Hello there!")
  val result = doSomething(original)
  println(result == original)
}

解决方法

您希望隐式函数在单个步骤中完成太多工作。

尝试再添加一个类型参数Out

implicit def genericInstance[T,HL <: HList,Out,LL <: HList](
  implicit
  gen: Generic.Aux[T,HL],myTypeclass: Lazy[MyTypeclass[HL]],//folder: LeftFolder.Aux[HL,(HNil,Int),leftFolder.type,(LL,Int)],folder: LeftFolder.Aux[HL,Out],ev: Out <:< (LL,// added
  reverse: Reverse.Aux[LL,HL]
): MyTypeclass[T] = (t: T) => {
  val generic = gen.to(t)
  val (transformed,idx) = myTypeclass.value.doSomething(generic)
  //val (ll,_) = transformed.foldLeft((HNil: HNil,0))(leftFolder)
  val (ll,_) = ev(transformed.foldLeft((HNil: HNil,0))(leftFolder))
  val reversed = reverse(ll)
  (gen.from(reversed),idx)
}

了解过度约束的隐式:

https://books.underscore.io/shapeless-guide/shapeless-guide.html#sec:type-level-programming:chaining(4.3依赖于链接的函数)

Scala shapeless Generic.Aux implicit parameter not found in unapply

Extract FieldType key and value from HList

How to implicitly figure out the type at the head of a shapeless HList

How to infer inner type of Shapeless record value with unary type constructor?