2016-08-10 3 views
4

Dies scheint eine klassische Frage für Entwickler zu sein, die mit Scala-Typ-Level-Programmierung arbeiten, aber ich konnte keine Lösung finden (oder weiß nicht, wie ich danach suchen soll) oder Muster dafür. Angenommen, ich habe eine Klasse wie folgt:Verwenden Sie den formlosen Mapper, ohne den Ergebnistyp angeben zu müssen

abstract class TypedTest[Args <: HList](implicit val optMapper: Mapped[Args, Option]) { 
    type OptArgs = optMapper.Out 

    def options: OptArgs // to be implemented by subclasses 
} 

ich Benutzer dieser Klasse will sie instanziiert mit einem HList Typ-Parametern (Args) und der Klasse eine Methode liefert eine HList Instanz enthält eine Instanz von jedem angegebenen Typ abrufen in einem Option (OptArgs). Ich verwende formlose Klasse Mapped dafür. Beachten Sie, dass I nicht eine Instanz von Args zur Instanziierungszeit bereitstellen muss.

Dieser Code funktioniert nicht, da der Compiler nicht den konkreten Typ von OptArgs ableitet und sogar eine offensichtlich korrekte Implementierung wie def options = HNil einen Kompilierungsfehler ergibt. Der gleiche Code des Aux-Muster mit:

abstract class TypedTest[Args <: HList, OptArgs <: HList](implicit val optMapper: Mapped.Aux[Args, Option, OptArgs]) { 
    def options: OptArgs 
} 

Dies zwingt mich beid Listen zum Zeitpunkt der Instanzierung angegeben werden, die die externe API unnötig ausführlichen machen. Gibt es einen Workaround dafür?

Antwort

0

Dies ist mein Verständnis, aber ich bin nicht 100% sicher und werde glücklich sein, zu korrigieren.

Der Typ member TypedTest.OptArgs ist kein abstrakter Typ, sondern ein Typalias. Es ist derselbe Typ für alle Unterklassen von TypedTest - ein Alias ​​für Mapped[Args, Option].Out, der ein abstrakter Typ ist und nicht mit irgendeinem Typ außer sich selbst vereinheitlicht werden kann. Wenn eine Unterklasse erstellt wird, wird der Typ member OptArgs nicht überschrieben.

Es wird klarer, wenn Mapped.Aux mit existentieller Art für Out0 verwenden, die IIUC mehr oder weniger äquivalent zu dem oben ist:

abstract class TypedTest[Args <: HList](
    implicit val optMapper: Mapped.Aux[Args, Option, T] forSome { type T }) { 

    type OptArgs = optMapper.Out 

    def options: OptArgs // to be implemented by subclasses 
} 

val intTest = new TypedTest[Int :: HNil] { 
    def options = Some(1) :: HNil 
} 

Error:(18, 29) type mismatch; 
found : shapeless.::[Some[Int],shapeless.HNil] 
required: this.OptArgs 
    (which expands to) T 
    def options = Some(1) :: HNil 

Leider bin ich keine Kenntnis von einer möglichen Lösung, mit der Ausnahme Zugabe Out als ein Typparameter oder definiere OptArgs als einen abstrakten Typ und spezifiziere ihn explizit in jeder Unterklasse.

+0

Ich habe nicht beabsichtigt, OptArgs zu einem abstrakten Typ, sondern eher ein Pfad abhängiger Typ - abhängig von dem Wert von 'optMapper'. Obwohl ich 'OptArgs' als Typalias definiert habe, konnte ich diese Definition weglassen und einfach' def options: optMapper.Out' schreiben. Pfadabhängige Typen können sinnvoll verwendet werden, auch wenn sie als abstrakte Typen definiert sind - das ist die Definition von "Mapper.Aux", um den pfadabhängigen Typ in einen Typparameter zu "konvertieren". Es kann jedoch sein, dass es in diesem Fall keine Lösung gibt. –

Verwandte Themen