HList foldLeft mit Tupel als Null

Nov 05 2020

Ich versuche foldLeftauf einer HList mit einem Akkumulator vom Typ (HL, Int), wo HLsich eine HList befindet. Das folgende Programm wird nicht kompiliert. Wenn ich jedoch zu einem einfacheren Akkumulator vom Typ wechsle HL(indem ich nur die kommentierten Zeilen gegen die oben genannten austausche), wird er kompiliert und funktioniert.

Das Umschließen einer HList in ein Tupel unterbricht die implizite Auflösung für den linken Ordner. Was vermisse ich?

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, (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, HNil, leftFolder.type, LL],
    folder: LeftFolder.Aux[HL, (HNil, Int), leftFolder.type, (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)
}

Antworten

2 DmytroMitin Nov 05 2020 at 06:20

Sie möchten, dass Implizite in einem einzigen Schritt zu viel Arbeit erledigen.

Versuchen Sie, einen weiteren Typparameter hinzuzufügen 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, (HNil, Int), leftFolder.type, Out],
  ev: Out <:< (LL, Int), // 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)
}

Lesen Sie mehr über überbeschränkte Implikationen:

https://books.underscore.io/shapeless-guide/shapeless-guide.html#sec:type-level-programming:chaining (4.3 Verkettungsabhängige Funktionen)

Scala formloser generischer Generic.Aux-Parameter, der nicht in unapply gefunden wurde

Extrahieren Sie den FieldType-Schlüssel und den Wert aus HList

Wie man implizit den Typ an der Spitze einer formlosen HList herausfindet

Wie kann man mit einem unären Typkonstruktor auf den inneren Typ des formlosen Datensatzwerts schließen?