HList foldLeft mit Tupel als Null
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
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?