2016-04-29 15 views
8

Sto utilizzando implicit def per creare un tipo ricorsivo HList, per abbinare diversi tipi di tipi più elevati di HList. Sono fortemente ispirato da this post.Scala tipi di tipi più elevati in implicit def falliscono con "impossibile trovare il valore implicito"

Questo codice è perfettamente funzionante:

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" 

Ora, io non so perché, ma se provo ad aggiungere un metodo ++ su HCons, che semplicemente le chiamate HList.++ metodo esistente, questo non funziona:

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) 
} 

ottengo questo errore di compilazione:

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

As HCons è un sottotipo di HList, come il tipo L1 definito da HList. ++, stavo pensando che fosse OK.

Ho provato questo ma non è lavorare meglio:

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))) 
    } 

Cosa mi sono perso?

Grazie :)

+0

Non ho provato a seguire quello che stai facendo, ma il ': HList' nella riga tre è una bandiera rossa. 'HList' è praticamente inutile come tipo statico per qualsiasi cosa. –

+0

Grazie, infatti, è sovraccarico di case classes che ereditano da HList – Loic

+0

L'ho rimosso per meno confusione ma il comportamento è lo stesso – Loic

risposta

10

Si dovrebbe cambiare il metodo di definizione ++ da questo:

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

a questo:

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

Il compilatore non ha abbastanza informazioni per selezionare il diritto valore implicito all'interno della definizione del metodo, ma quando si passa l'appender dall'esterno, questo esempio deve passare:

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

Update 1: Nel metodo ++ su HCons, chiamiamo il metodo HList.++ che richiede un parametro implicito. Questo parametro deve essere di tipo Appender[HCons[T, U], L2, HCons[T, U#Plus[L2]]]. Il compilatore può riempire questo parametro implicito da HList.consAppender, ma a sua volta richiede un altro parametro implicito di tipo Appender[U, L2, U#Plus[L2]]. Questo è il parametro che il compilatore non può scoprire da solo. Sapendo questo, il codice precedente può essere semplificata:

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

Update 2: Il compilatore deve compilare parametri impliciti nel sito chiamata, nel nostro caso all'interno HCons.++ metodo (può essere verificata, per esempio, con scalac -Xprint:typer). Si può scegliere tra impliciti forniscono due tipi Appender:

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

Il primo può essere utilizzato solo se il tipo di parametro U è HNil, l'altro soltanto quando U è HCons. Ma questa informazione non è disponibile all'interno di HCons.++. Lo sa solo che U <: HList ma non sa quale implementazione di HList è e quindi non riesce.

+0

Grazie! Sta funzionando!! Meraviglioso :) – Loic

+0

La versione 1 dell'aggiornamento non funziona: non è stato possibile trovare il valore implicito per il parametro f: Appender [HCon [T, U], L2, HCons [T, U] #Plus [L2]] – Loic

+0

Non so dove sia il problema questo, funziona per me (usando Scala 2.11.6). Potremmo approfondire la questione, ma sono felice finché la prima versione funzionerà per te. – Mifeet