Имея HList T0 :: T1 :: ... Tn и тип R, можно ли вывести тип функции T0 => T1 ... => Tn => R? - PullRequest
0 голосов
/ 15 ноября 2018

Я хочу создать что-то, что будет работать так

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 голосов
/ 16 ноября 2018

Что ж, после проверки того, как эта проблема очень похожа на реверсирование 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 без синтаксического сахара, поскольку в противном случае компилятор считает, что я передаю неявное явно.

0 голосов
/ 15 ноября 2018

Я не думаю, что это напрямую возможно с использованием некоторых классов типов в 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 }

Таким образом, вам не нужно указывать типы аргументов для функции, скорее, сама функция должна принимать кортеж, что также немного эргономично, потому что вам придется использовать частичную функциюсинтаксис для распаковки аргументов из аргумента кортежа.

...