2016-04-29 5 views
8

Ich verwende implizite Def, um eine rekursive HList Art zu erstellen, um mehrere Arten von höher verbundenen Typen von HList zu entsprechen. Ich bin stark inspiriert von this post.Scala höher kinked Typen im impliziten Def fehlschlägt mit "Konnte nicht impliziten Wert finden"

Dieser Code perfekt funktioniert:

sealed trait HList { 
    type Plus[L <: HList] <: HList 
} 

class HNil extends HList { 
    type Plus[L <: HList] = L 

    def ::[T](v: T) = HCons(v, this) 
} 

case class Appender[L1 <: HList, L2 <: HList, R <: HList](fn: (L1, L2) => R) { 
    def apply(l1: L1, l2: L2) = fn(l1, l2) 
} 

object HNil extends HNil 

object HList { 
    def ++[L1 <: HList, L2 <: HList](l1: L1, l2: L2)(implicit f: Appender[L1, L2, L1#Plus[L2]]): L1#Plus[L2] = f(l1, l2) 

    implicit def nilAppender[L <: HList]: Appender[HNil, L, L] = Appender((v: HNil, l: L) => l) 

    implicit def consAppender[T, L1 <: HList, L2 <: HList, R <: HList](implicit f: Appender[L1, L2, R]): Appender[HCons[T, L1], L2, HCons[T, R]] = { 
    Appender[HCons[T, L1], L2, HCons[T, R]]((l1: HCons[T, L1], l2: L2) => HCons(l1.head, f(l1.tail, l2))) 
    } 
} 

case class HCons[T, U <: HList](head: T, tail: U) extends HList { 
    type Plus[L <: HList] = HCons[T, U#Plus[L]] 

    def ::[V](v: V) = HCons(v, this) 
} 

import HList._ 

val hlist1 = 2.0 :: "hi" :: HNil 
val hlist2 = 1 :: HNil 

val sum = ++(hlist1, hlist2) 
println("last element : " : + sum.tail.tail.head) // prints last element : 1" 

Nun, ich weiß nicht warum, aber wenn ich versuche, eine ++ Methode auf HCons, hinzuzufügen, die HList.++ Methodenaufrufe einfach bestehende, dies nicht funktioniert:

case class HCons[T, U <: HList](head: T, tail: U) extends HList { 
type Plus[L <: HList] = HCons[T, U#Plus[L]] 

    def ::[V](v: V) = HCons(v, this) 

    def ++[L2 <: HList](l2: L2) = HList.++(this,l2) 
} 

ich erhalte diese Kompilierungsfehler:

could not find implicit value for parameter f: Appender[HCons[T,U],L2,HCons[T,U]#Plus[L2]] 

Da HCons ein Untertyp von HList ist, wie der L1-Typ, der von HList. ++ definiert wird, dachte ich, es sei OK.

Ich habe dies versucht, aber, dass die Arbeit ist nicht besser:

implicit def consAppender[T, L1 <: HList, L2 <: HList, L3, R <: HList](implicit f: Appender[L1, L2, R], ev: L3 <:< HCons[T, L1]): Appender[HCons[T, L1], L2, HCons[T, R]] = { 
    Appender[HCons[T, L1], L2, HCons[T, R]]((l1: L3, l2: L2) => HCons(l1.head, f(l1.tail, l2))) 
    } 

Was habe ich verpasst?

def ++[L2 <: HList](l2: L2) = HList.++(this,l2) 

dazu:

Thanks :)

+0

Ich habe nicht versucht zu folgen, was Sie tun, aber die ': HList' in Zeile drei ist eine rote Fahne. 'HList' ist als statischer Typ für irgendetwas ziemlich nutzlos. –

+0

Danke, in der Tat ist es von Fallklassen überladen von HList – Loic

+0

Ich habe es für weniger Verwirrung entfernt, aber das Verhalten ist das gleiche – Loic

Antwort

10

Sie sollten Ihre ++ Methodendefinition aus dieser Änderung

def ++[L2 <: HList](l2: L2)(implicit f: Appender[HCons[T,U], L2, Plus[L2]]) = HList.++(this,l2) 

Der Compiler nicht genügend Informationen hat das Recht zu wählen impliziter Wert innerhalb der Methodendefinition, aber wenn Sie den Appender von außen übergeben, sollte dieses Beispiel bestehen:

val hlist1 = 2.0 :: "hi" :: HNil 
val hlist2 = 1 :: HNil 
println(hlist1++hlist2) 

Update 1: Im ++ Methode auf HCons, rufen wir die HList.++ Methode, die einen impliziten Parameter erfordert. Dieser Parameter muss vom Typ Appender[HCons[T, U], L2, HCons[T, U#Plus[L2]]] sein. Der Compiler könnte diesen impliziten Parameter von HList.consAppender füllen, aber dies erfordert wiederum einen anderen impliziten Parameter vom Typ Appender[U, L2, U#Plus[L2]]. Dies ist der Parameter, den der Compiler nicht selbst finden kann.

def ++[L2 <: HList](l2: L2)(implicit f: Appender[U, L2, U#Plus[L2]]): Plus[L2] = HList.++(this, l2) 

Update 2: Mit diesem Wissen können Sie den obigen Code zu vereinfachen Der Compiler in implizite Parameter an der Aufrufstelle füllen müssen, in unserem Fall innerhalb HCons.++ Methode (kann überprüft werden, zum Beispiel mit scalac -Xprint:typer). Es kann aus implicits wählen zwei Bereitstellung appender Typen:

Appender[HNil, L, L] 
Appender[HCons[T, L1], L2, HCons[T, R]] 

Die erste nur U wenn Typ-Parameter HNil verwendet werden kann, nur die andere, wenn UHCons ist. Aber diese Information ist nicht innerhalb verfügbar. Es weiß nur, dass U <: HList aber nicht weiß, welche Implementierung von HList es ist und daher fehlschlägt.

+0

Vielen Dank! Es funktioniert!! Wonderful :) – Loic

+0

Update 1 Version funktioniert nicht: konnte keinen impliziten Wert für Parameter f finden: Appender [HCons [T, U], L2, HCons [T, U] #Plus [L2]] – Loic

+0

Nicht sicher, wo das Problem ist das funktioniert bei mir (mit Scala 2.11.6). Wir könnten tiefer hinein graben, aber ich bin glücklich, solange die erste Version für dich arbeitet. – Mifeet

Verwandte Themen