2016-07-04 12 views
2

Ich versuche, ein generisches Merkmal 'Repo' für einige Typen zu erstellen, die Subtypen eines Merkmals 'Identifizierbar' sind. Mein Plan ist es, Implementierer von "Repo" zu instanziieren, indem ich einen generischen TypTag [HList] übergebe, der die "identifizierbaren" Subtypen beschreibt.Obere Grenze für HList-Typen zum Kompilierungszeitpunkt erzwingen

Wie kann ich dem Compiler garantieren, dass die Typen, die im HList übergeben werden, Subtypen des Merkmals "Identifizierbar" sind?

Hier ist, was ich bisher habe:

//All types in HList must extend Identifiable, how to enforce that at compile time? 
    trait Repo { 
     implicit val ltag: TypeTag[L] forSome {type L <: HList} 
     .. 
    } 

    trait Identifiable { 
     .. 
    } 

    case class Person(..) extends Identifiable 
    case class Address(..) 

    //This should compile 
    class MyRepo 
     (implicit val ltag: TypeTag[Person :: HNil]) 
     extends Repo { 
     .. 
    } 

    //This should not 
    class MyRepo 
     (implicit val ltag: TypeTag[Address :: HNil]) 
     extends Repo { 
     .. 
    } 
//HList can contain an unknown number of types 

ich diese Frage gesehen habe, die verwandt zu sein scheint: Type inference on contents of shapeless HList Unterschied ist ich arbeite nicht eine Implementierung des hList muß mit so nicht sicher, wie ich die Obergrenze nur mit Typen berechnen kann.

Antwort

3

Es gibt eine ganze Reihe von Einschränkungen auf HList von https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/hlistconstraints.scala zur Verfügung gestellt.

Die, die Sie suchen, ist wahrscheinlich LUBConstraint. Unter Angabe der Dokumentation:

Typklasse Bezeugen, dass jedes Element von L ein Subtyp von B ist.

Um zu verwenden, müssen Sie nur den impliziten Nachweis eines LUBContraint[L, Identifiable] erfordern.

z.

trait Repo[L <: HList] { 
    implicit val ltag: TypeTag[L] 
    implicit val ev: LUBConstraint[L, Identifiable] 
} 

trait Identifiable 
case class Person(name: String) extends Identifiable 
case class Address(street: String) 

type P = Person :: HNil 
class MyPersonRepo(implicit 
    val ltag: TypeTag[P], 
    val ev: LUBConstraint[P, Identifiable] 
) extends Repo[P] 


type A = Address :: HNil 
class MyAddressRepo(implicit 
    val ltag: TypeTag[A], 
    val ev: LUBConstraint[A, Identifiable] 
) extends Repo[A] 

new MyPersonRepo // this works 
new MyAddressRepo // this doesn't 

Wenn Sie bereit sind, anstelle eines Merkmals eine abstrakte Klasse zu verwenden, können Sie alles schöner

abstract class Repo[L <: HList](implicit 
    val ltag: TypeTag[L], 
    val ev: LUBConstraint[L, Identifiable] 
) 

trait Identifiable 
case class Person(name: String) extends Identifiable 
case class Address(street: String) 

type P = Person :: HNil 
class MyPersonRepo extends Repo[P] 

type A = Address :: HNil 
class MyAddressRepo extends Repo[A] 

Jetzt machen Sie den Fehler sofort erhalten, wenn die Klasse erweitern.

+0

Was wäre die Syntax, um diesen Beweis zu einem Merkmal von einem Implementierer zu übertragen? Ich kann keine Typparameter an das Merkmal übergeben, daher fällt es mir schwer, meinen Kopf darum zu wickeln. – eirirlar

+0

@eirirlar Siehe das obige Beispiel –

+0

Ehrfürchtig, vielen Dank – eirirlar

Verwandte Themen