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

时间:2018-11-15 16:52:39

标签: scala implicit shapeless hlist

我想创建类似这样的东西

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

    }
}

以便它像这样

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

2 个答案:

答案 0 :(得分:1)

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

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)

不幸的是,您仍然必须为函数类型显式指定参数。从理论上来说可以 定义扩展类:

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

但是,这将需要您“提前”知道结果类型,这是非常不符合人体工程学的(并且不能真正与上面的定义一起使用,但是可以使它与更多的代码一起使用)

您可以通过要求函数接受一个元组来克服最后一个问题:

  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 }

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

答案 1 :(得分:0)

好吧,在检查了这个问题看起来与实际反转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来调用它而没有语法糖,因为否则编译器会认为我正在显式传递隐式。