2013-06-27 6 views
6

Ich arbeite an einer kleinen Bibliothek für Wirtschaftsmodelle, die die Einheiten der Entitäten überprüfen, mit Typen, z. anstelle von val apples = 2.0 schreiben wir val apples = GoodsAmount[KG, Apples](2.0). Zum Erstellen von Warenbündeln versuche ich HLists aus der formlosen Bibliothek zu verwenden. Das funktioniert gut, aber in manchen Fällen kann ich nicht so generisch sein, wie ich es vorziehe. Siehe z.B. das folgende Problem.Shapeless: Überprüfen von Typeneinschränkungen von polymorphen Funktionen

Ich beginne mit einem einfachen Code, der erklärt, was ich in formlos heben möchte. Wir erstellen zwei Klassen, auf denen Km steht, die anderen Meilen. Es sollte erlaubt sein, Km-Klassen hinzuzufügen, aber keine Meilen. Dass ich einen abstrakten Typ T verwende, ist hauptsächlich motiviert durch unsere komplexere Bibliothek. Und der indirekte Aufruf an die '+' Funktion ist nur, weil wir etwas Ähnliches im formlosen Fall brauchen.

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 */ 
} 

Dies funktioniert wie vorgesehen. Aber es ist notwendig, die Typ-Contraint-Prüfung für die 'Hinzufügen'-Funktion durchzuführen. Mein Versuch, dies zu HLists erweitern sieht wie folgt aus:

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

Aber diese erzeugen die folgende Fehlermeldung (mit 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) 

Der erste Fehler behoben werden soll, im Fall dass ich der caseTuple-Funktion eine Type-Einschränkung hinzufügen könnte, aber um ehrlich zu sein, habe ich nicht verstanden, wie die at-Funktion funktioniert und wo ich den impliziten evidence-Parameter hinzufügen könnte. Und ich weiß auch nicht, was ich tun muss, damit der Mapper seinen impliziten Wert findet.

Eine weniger generische Version, wo ich die caseTuple Funktion replase mit

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

funktioniert gut, aber müßte viele redundanten Code schreiben (okay, diese Lösung wäre immer noch besser sein als unsere aktuelle Lösung mit Tupel). Kann mir jemand einen Hinweis geben, wie ich dieses Problem lösen kann?

Danke, Klinke

+0

Sie könnten versuchen, Ihr 'Foo' so zu definieren:' Merkmal Foo [T <: Foo] {v: Double; + (t T): T = ...} '. 'Klasse Km (val v: Double) erweitert Foo [Km]'. 'implizit def add [T] = bei [(Foo [T], Foo [T])]' – senia

Antwort

7

Sie die Elemente vom Typ benötigen, können durch Hinzufügen eines Typ-Parameter auf den Fall entsprechen:

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

Oder Sie könnten verwenden, um eine existenzielle Art, da man eigentlich nur darauf, dass Sie sind gleich:

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

Jede Version wird das gewünschte Verhalten bieten.

+0

Danke, das funktioniert auch mein komplexerer Fall ;-) Aber ich habe immer noch das Problem mit dem fehlenden impliziten Wert für die Mapper. Ich werde versuchen, es selbst zu lösen, aber vielleicht kannst du mir auch hier helfen? – Klinke

+0

Okay, habe eine Lösung für die vereinfachte Version gefunden, indem ich einen Kontext für A hinzugefügt habe, um zu helfen. Also habe ich jetzt 'implizit def caseTuple [_t, A <: Foo {Typ T = _T} <% Foo {Typ T = _T}] = ...' Diesmal übersetzt sich die Lösung nicht so einfach in meine Vollversion , aber hoffentlich kann ich auch die neuen Probleme beheben. – Klinke

+0

@Klinke: Ich bin mir nicht sicher, ob ich das Problem verstehe - wenn ich mein 'add' in mein' ExampleShapeless' kopiere und einfüge, scheint alles wie erwartet zu funktionieren. –

Verwandte Themen