2013-06-27 12 views
6

Sto lavorando su una piccola libreria per i modelli economici che controllano le unità delle entità, utilizzando i tipi, ad es. invece di val apples = 2.0 scriviamo val apples = GoodsAmount[KG, Apples](2.0). Per creare un pacchetto di merci, provo a usare le liste HL dalla libreria senza forma. Funziona bene, ma in alcuni casi non posso essere un codice generico come preferisco. Vedi per es. il seguente problemaSenza forma: controllo dei vincoli di tipo delle funzioni polimorfiche

Inizio con un semplice codice che spiega quello che voglio sollevare in informe. Creiamo due classi, che rappresentano Km, le altre miglia. Dovrebbe essere consentito aggiungere classi Km, ma non miglia. Che io usi un tipo astratto T è principalmente motivato dalla nostra libreria più complessa. E la chiamata indiretta alla funzione '+' è solo perché abbiamo bisogno di qualcosa di simile nel caso senza forma dietro.

trait Foo { 
    type T 
    val v: Double 
    def +[B <: Foo](other: B)(implicit ev: this.T =:= other.T) = v + other.v 
} 

trait _Km 
trait _Miles 

case class Km(v: Double) extends Foo { type T = _Km } 
case class Miles(v: Double) extends Foo { type T = _Miles } 

object ExampleSimple extends App { 
    def add[A <: Foo, B <: Foo](a: A, b: B)(implicit ev: a.T =:= b.T) = { a + b } 

    add(Km(1), Km(2)) 
    // add(Km(1), Miles(2)) /* does not compile as intended */ 
} 

Questo funziona come previsto. Ma è necessario avere il controllo di tipo Contraint sulla funzione 'aggiungi'. Il mio tentativo di estendere questo per HLists assomiglia a questo:

object ExampleShapeless extends App { 
    import shapeless._ 

    val l1 = Km(1) :: Km(2) :: HNil 
    val l2 = Km(4) :: Km(3) :: HNil 

    object add extends Poly1 { 
    implicit def caseTuple[A <: Foo] = at[(A,A)] { case (a, b) => a + b } 
    } 

    (l1 zip l2).map(add) 
} 

Ma questo genera il seguente messaggio di errore (utilizzando Scala 2.10.2):

[error] /home/fuerst/gitg3m/code/types/src/main/scala/lagom_d/extract.scala:50: Cannot prove that a.T =:= b.T. 
[error]  implicit def caseTuple[A <: Foo] = at[(A,A)] { case (a: Foo, b) => a + b } 
[error]                  ^
[error] /home/fuerst/gitg3m/code/types/src/main/scala/lagom_d/extract.scala:54: could not find implicit value for parameter mapper: shapeless.Mapper[ExampleShapeless.add.type,shapeless.::[(Km, Km),shapeless.::[(Km, Km),shapeless.HNil]]] 
[error] (l1 zip l2).map(add) 

Il primo errore deve essere fissato, nel caso che potrei aggiungere un Type Constraint alla funzione caseTuple, ma per essere onesti, non ho capito come funziona la funzione at e dove potrei aggiungere il parametro evidence implicito. E anche io non so, cosa devo fare, in modo che il Mapper trovi il suo valore implicito.

Una versione meno generico, dove ho replase la funzione caseTuple con

implicit def caseTuple = at[(Km,Km)] { case (a, b) => a + b } 

funziona bene, ma avrebbe bisogno di scrivere un sacco di codice ridondante (ok, questa soluzione sarebbe ancora meglio come la nostra soluzione corrente utilizzando tuple). Qualcuno può darmi un suggerimento su come posso risolvere questo problema?

Grazie, Klinke

+0

Si potrebbe provare a definire il proprio 'Foo' in questo modo:' trait Foo [T <: Foo] {v: Double; + (t T): T = ...} '. 'classe Km (val v: Double) estende Foo [Km]'. 'implicit def add [T] = at [(Foo [T], Foo [T])]' – senia

risposta

7

È possibile richiedere i membri di tipo in modo che corrisponda con l'aggiunta di un parametro di tipo al caso:

object add extends Poly1 { 
    implicit def caseTuple[_T, A <: Foo { type T = _T }] = at[(A, A)] { 
    case (a, b) => a + b 
    } 
} 

Oppure si potrebbe usare un tipo esistenziale, dal momento che solo veramente cura che sono gli stessi:

object add extends Poly1 { 
    implicit def caseTuple[A <: Foo { type T = _T } forSome { type _T }] = 
    at[(A, A)] { 
     case (a, b) => a + b 
    } 
} 

Ciascuna versione fornirà il comportamento desiderato.

+0

Grazie, questo funziona bene anche il mio caso più complesso ;-) Ma ho ancora il problema con il valore implicito mancante per il Mapper. Cercherò di risolverlo da solo, ma forse puoi aiutarmi anche qui? – Klinke

+0

Ok, ho trovato una soluzione per la versione semplificata, aggiungendo un contesto vincolato per A che viene in aiuto. Quindi ho ora 'implicit def caseTuple [_t, A <: Foo {tipo T = _T} <% Foo {tipo T = _T}] = ...' Questa volta la soluzione non si traduce così facilmente nella mia versione completa , ma spero di poter risolvere anche i nuovi problemi. – Klinke

+0

@Klinke: Non sono sicuro di aver capito il problema: se copio e incollo il mio 'add' in' ExampleShapeless' tutto sembra funzionare come previsto. –

Problemi correlati