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