给定一个 HList T0::T1:: ... Tn 和类型 R 是否可以推断出函数类型 T0=>T1 ...=> Tn => R?

Given a HList T0::T1:: ... Tn and type R is it possible to infer a function type T0=>T1 ...=> Tn => R?

我想创建这样的东西

implicit class HListOps[AHList<:HList](value:AHList){
    def fold[R](folder: /*What here?*/)={

    }
}

所以它像这样工作

("HeY"::42::HNil).fold{string=>int=> string+int.toString} // returns "HeY42"

我不认为在 Shapeless 中直接使用某些类型 classes 是可能的,但是可以对类型 (T0, T1, ...) => R:[=14= 的函数做类似的事情]

implicit class HListOps[L <: HList](value: L) {
  def fold[R, F](folder: F)(implicit ftp: FnToProduct.Aux[F, L => R]): R = {
    ftp(folder).apply(value)
  }
}

(1 :: "a" :: HNil).fold((x: Int, y: String) => y + x)

遗憾的是,您仍然必须明确指定函数类型的参数。它可以理论上可以定义扩展class到这个:

implicit class HListOps2[L <: HList, F, R](value: L)(implicit ftp: FnToProduct.Aux[F, L => R]) {
  def fold(folder: F): R = ftp(folder).apply(value)
}

然而,这将要求您知道结果类型 "in advance",这是非常不符合人体工程学的(并且不会真正适用于上面的定义,但可以使其稍微适用更多代码)。

您可以通过要求函数改为接受元组来解决最后一个问题:

  implicit class HListOps3[L <: HList, T](value: L)(implicit tup: Tupler.Aux[L, T]) {
    def fold[R](folder: T => R): R = folder(tup(value))
  }

  (1 :: "a" :: HNil).fold { case (x, y) => y + x }

这样,您就不需要为函数指定参数类型,而是函数本身应该接受一个元组,这也符合人体工程学,因为您将不得不使用部分函数语法来从元组参数中解压参数。

好吧,在检查了这个问题看起来与实际反转 HList 非常相似之后,我使用了一种非常相似的方法并使它起作用:

import java.time.LocalDate

import scala.language.{higherKinds, reflectiveCalls}

import cats.Applicative
import shapeless._

trait HFolder[InL <: HList, Out] {
  type Fld
  def fold(inL: InL): Function1[Fld, Out]
}

object HFolder {

  implicit def nilHFolder[Out]: HFolder[HNil, Out] {type Fld = Out} = new HFolder[HNil, Out] {
    type Fld = Out

    override def fold(inL: HNil): Function1[Out, Out] = identity
  }

  implicit def consHFolder[InH, InT <: HList, Out]
  (implicit tailHFolder: HFolder[InT, Out]): HFolder[InH :: InT, Out] {type Fld = InH => tailHFolder.Fld} =
    new HFolder[InH :: InT, Out] {
      override type Fld = InH => tailHFolder.Fld

      override def fold(inL: InH :: InT): Function1[InH => tailHFolder.Fld, Out] = {
        folder =>
          inL match {
            case inH :: inT => tailHFolder.fold(inT)(folder(inH))
          }
      }
    }

  implicit class HListOps[InL <: HList](inL: InL) {
    def fold[Out](implicit hfolder: HFolder[InL, Out]): Function[hfolder.Fld, Out] = {
      hfolder.fold(inL)
    }

  }

  //Here compiler can infer correct info (in this case (String=>Int=>LocalDate)=>LocalDate)
  val folder= ("Alejo" :: 29 :: HNil).fold[LocalDate] 


}

唯一的小问题是,在调用方法 fold 之后,我必须使用 apply 这个词来调用它,而不使用语法糖,否则编译器会认为我正在显式传递隐式。