Ho dormito troppo tardi la scorsa notte cercando di capire questo problema di Shapeless e temo che mangerà la mia serata se non lo tolgo il mio petto, quindi ecco qui.Comportamento strano che cerca di convertire ricorsivamente case class class in liste eterogenee con Shapeless
In questa versione minimizzata io sono solo la definizione di una classe tipo che ricorsivamente convertire classi case in heterogeneous liste:
import shapeless._
trait DeepHLister[R <: HList] extends DepFn1[R] { type Out <: HList }
trait LowPriorityDeepHLister {
type Aux[R <: HList, Out0 <: HList] = DeepHLister[R] { type Out = Out0 }
implicit def headNotCaseClassDeepHLister[H, T <: HList](implicit
dht: DeepHLister[T]
): Aux[H :: T, H :: dht.Out] = new DeepHLister[H :: T] {
type Out = H :: dht.Out
def apply(r: H :: T) = r.head :: dht(r.tail)
}
}
object DeepHLister extends LowPriorityDeepHLister {
implicit object hnilDeepHLister extends DeepHLister[HNil] {
type Out = HNil
def apply(r: HNil) = HNil
}
implicit def headCaseClassDeepHLister[H, R <: HList, T <: HList](implicit
gen: Generic.Aux[H, R],
dhh: DeepHLister[R],
dht: DeepHLister[T]
): Aux[H :: T, dhh.Out :: dht.Out] = new DeepHLister[H :: T] {
type Out = dhh.Out :: dht.Out
def apply(r: H :: T) = dhh(gen.to(r.head)) :: dht(r.tail)
}
def apply[R <: HList](implicit dh: DeepHLister[R]): Aux[R, dh.Out] = dh
}
Proviamo it out! In primo luogo abbiamo bisogno di alcuni classi case:
case class A(x: Int, y: String)
case class B(x: A, y: A)
case class C(b: B, a: A)
case class D(a: A, b: B)
E poi (si noti che ho ripulito la sintassi tipo per il bene di questo non essere un pasticcio del tutto illeggibile):
scala> DeepHLister[A :: HNil]
res0: DeepHLister[A :: HNil]{
type Out = (Int :: String :: HNil) :: HNil
} = [email protected]
scala> DeepHLister[B :: HNil]
res1: DeepHLister[B :: HNil] {
type Out = (
(Int :: String :: HNil) :: (Int :: String :: HNil) :: HNil
) :: HNil
} = [email protected]
scala> DeepHLister[C :: HNil]
res2: DeepHLister[C :: HNil] {
type Out = (
((Int :: String :: HNil) :: (Int :: String :: HNil) :: HNil) ::
(Int :: String :: HNil) ::
HNil
) :: HNil
} = [email protected]
Fin qui tutto bene. Ma poi:
scala> DeepHLister[D :: HNil]
res3: DeepHLister[D :: HNil] {
type Out = ((Int :: String :: HNil) :: B :: HNil) :: HNil
} = [email protected]
Il B
non ha ottenuto convertito. Se si accende -Xlog-implicits
questo è l'ultimo messaggio:
<console>:25: this.DeepHLister.headCaseClassDeepHLister is not a valid implicit value for DeepHLister[shapeless.::[B,shapeless.HNil]] because:
hasMatchingSymbol reported error: diverging implicit expansion for type DeepHLister[this.Repr]
starting with method headNotCaseClassDeepHLister in trait LowPriorityDeepHLister
DeepHLister[D :: HNil]
^
Il che non ha senso per me- headCaseClassDeepHLister
dovrebbe essere in grado di generare DeepHLister[B :: HNil]
bene, e lo fa se chiedete direttamente.
Questo succede sia su 2.10.4 che su 2.11.2, sia con versione 2.0.0 sia master. Sono abbastanza sicuro che questo deve essere un bug, ma non escludo la possibilità che sto facendo qualcosa di sbagliato. Qualcuno ha mai visto qualcosa di simile prima? C'è qualcosa di sbagliato nella mia logica o qualche restrizione su Generic
Mi manca?
Ok, grazie per l'ascolto, forse ora posso andare a leggere un libro o qualcosa del genere.
Ho un problema simile con 'DeepLister [B :: HNil]'. Ho appena copiato il codice 5 minuti fa, quindi esaminerò ulteriormente. – EECOLOR
Questo sarebbe abbastanza facile con macro whitebox. –
@ GuillaumeMassé, certo, ma l'obiettivo è limitare l'uso di macro ai pochi fondamentali forniti da Shapeless. –