2015-03-29 11 views
5

Sto cercando di rendere questo esempio piuttosto sciocco, progettando di estenderlo a qualcosa di più significativo.Shapeless: IsHCons, implicito non trovato

ma senza fortuna finora: ottengo could not find implicit value for parameter ihc

Che cosa mi manca?

sealed trait Field[T] { def name: String } 
case class IntegerField(name: String) extends Field[Int] 
val year = IntegerField("year") 

val test = (year :: 23 :: HNil) :: (year :: 2 :: HNil) :: HNil 

type TypedMap = IntegerField :: Int :: HNil 

def get[L <: HList : <<:[TypedMap]#λ] 
     (key: IntegerField, list: L) 
     (implicit ihc: IsHCons.Aux[L, TypedMap, L] 
    ): Option[Int] = { 

    if(list == HNil) return None 
    val elem: TypedMap = list.head 
    if(elem.head == key) Some(elem.tail.head) 
    else get(key, list.tail) 

} 

get(year, test) 

risposta

11

Quando si scrive IsHCons.Aux[L, TypedMap, L] stai chiedendo la prova che il HList L ha la testa e la coda TypedMapL, il che significherebbe che si tratta di un HList infinita, che non è possibile, dal momento che alla Scala non consente questo tipo di tipo arbitrariamente ricorsivo (prova a scrivere qualcosa come type Foo = Int :: Foo, ad esempio, riceverai un errore "riferimento ciclico illegale"). Probabilmente non è nemmeno quello che vuoi.

In generale è improbabile che si usi IsHCons molto in Shapeless, poiché è quasi sempre meglio indicare solo la struttura che si desidera nel tipo. Ad esempio, i seguenti due definizioni fanno la stessa cosa:

import shapeless._, ops.hlist.IsHCons 

def foo[L <: HList](l: L)(implicit ev: IsHCons[L]) = ev.head(l) 

E:

def foo[H, T <: HList](l: H :: T) = l.head 

Ma il secondo è ovviamente preferibile (è chiaro, non richiede un'istanza supplementare tipo di classe di essere trovato in fase di compilazione, ecc.), ed è quasi sempre possibile scrivere qualunque cosa tu stia cercando di fare in quel modo.

Si noti inoltre che richiedono un'istanza IsHCons significa che la ricorsione qui non funziona come previsto, non si può chiamare get su un HNil, dal momento che il compilatore non può provare che si tratta di un HCons (perché non è).

Sei sicuro di aver bisogno di una lista? Se si richiede che tutti i membri della hlist siano di tipo TypedMap, è possibile utilizzare anche lo Sized di Shapeless (se si desidera che il tipo acquisisca la lunghezza) o anche solo un semplice vecchio List.

Se davvero, vuole veramente utilizzare un HList qui, io suggerirei di scrivere una nuova classe Tipo:

trait FindField[L <: HList] { 
    def find(key: IntegerField, l: L): Option[Int] 
} 

object FindField { 
    implicit val findFieldHNil: FindField[HNil] = new FindField[HNil] { 
    def find(key: IntegerField, l: HNil) = None 
    } 

    implicit def findFieldHCons[H <: TypedMap, T <: HList](implicit 
    fft: FindField[T] 
): FindField[H :: T] = new FindField[H :: T] { 
    def find(key: IntegerField, l: H :: T) = if (l.head.head == key) 
     Some(l.head.tail.head) 
    else fft.find(key, l.tail) 
    } 
} 

def get[L <: HList](key: IntegerField, l: L)(implicit 
    ffl: FindField[L] 
): Option[Int] = ffl.find(key, l) 

E poi:

scala> get(IntegerField("year"), test) 
res3: Option[Int] = Some(23) 

scala> get(IntegerField("foo"), test) 
res4: Option[Int] = None 

Questo è un modello abbastanza comune in Senza forma, induttivamente descrivi come eseguire un'operazione su una hlist vuota, quindi su una hlist con una testa anteposta a una coda che sai come eseguire l'operazione, ecc.

+0

Grazie! So che in questo modo sembra stupido. Il mio obiettivo è costruire qualcosa come HMap' senza forma, ma con una funzione 'get()' che accetta un'istanza generica 'Field [T]' invece di un 'IntegerField',' StringField', ... Nel tuo esempio , come si ricorre se l'oggetto che si sta cercando non è il primo? Se provo a scrivere 'else find (key, l.tail)' ottengo 'found: T, richiesto: shapeless. :: [H, T],' --- ecco perché stavo cercando 'IsHCons.Aux' – kenshin

+0

Ah, buona cattura, ho digitato male e ho aggiornato 'findFieldHCons' in modo che funzioni effettivamente. –

+0

Fantastico! Persino la versione non dumbed funziona come un incantesimo. Grazie :) – kenshin