2016-07-31 26 views
0

Wie kann ich das erreichen:scala Typparameter als Objekttyp

final case class ChairId(id: String) 

trait GeneratorLike[TO, TC <: AbstractId] { 
    val prefix: String 
    def generate(): TC = TO.apply(prefix + "-" + UUID.randomUUID()) 
} 

implicit object ChairIdGenerator extends GeneratorLike[ChairId.type, ChairId] { 
    val prefix: String = "CHAIR" 
} 

implicit def IdFn[TO, TC <: AbstractId](x: TO)(implicit ev: GeneratorLike[TO, TC]): GeneratorLike[TO, TC] = ev 

//right now I can call: 
ChairId.generate() 

Ich mag nicht für diese Situation definieren Begleitobjekt, und ich fragte mich, ob es eine Chance gibt Objekt mit der Verwendung von implicits zu verlängern?

Wenn ich das tue (ich benutze TO als TypeObject und TC als TypeClass Benennung) idFn[TO, TC] Ich möchte TO Objekt sein, das implementiert def apply(id: String): TC kann ich das erzwingen? Und wie würde ich diese Funktion nutzen? Es fühlt sich völlig unmöglich Funktion auf Typ-Parametern zu nennen:/

+0

aufrufen können Sie nicht anrufen Eine * Methode * für einen Typparameter. – pedrofurla

Antwort

2

Es unmöglich ist, ein Verfahren für einen Typ-Parameter zu nennen, weil es einen Typen und keine Objekt darstellt. Sie können eine Methode für ein Objekt aufrufen, weil es etwas gibt, das existiert, aber ein Typ ist ein abstraktes Konzept. Ich weiß nicht, was Ihre Motivation ist, implizit generate() zu Companion-Objekten hinzuzufügen, weil es tatsächlich genauso viel Code benötigt, um eine implizite GeneratorLike zu definieren, als es den Begleiter für ChairId zu definieren.

Wenn Sie erzwingen GeneratorLike, um eine apply-Methode (die durch Case-Klasse apply implementiert werden kann), und den ersten Typ Parameter zu entfernen, wird dies funktionieren.

trait GeneratorLike[TC <: AbstractId] { this: Singleton => 
    val prefix: String 
    def apply(id: String): TC 
    def generate(): TC = apply(prefix + "-" + UUID.randomUUID()) 
} 

abstract class AbstractId 

final case class ChairId(id: String) extends AbstractId 

object ChairId extends GeneratorLike[ChairId] { 
    val prefix = "CHAIR" 
} 

scala> ChairId.generate() 
res0: ChairId = ChairId(CHAIR-60bb01c7-af95-46c7-af45-0b3fa78b3080) 
+0

Ich habe Fall Klassen und Objekte automatisch generiert, deshalb wollte ich das nicht ändern. Ihre Lösung ist ohne Zweifel schön und sauber. – kpbochenek

+0

'implizite Def IdFn [TC <: AbstractId] (zu: {def apply (s: String): TC}) (impliziter ev: GeneratorLike [TC]): SpecGeneratorLike [TC] = neuer SpecGeneratorLike [TC] (ev, to) Klasse GeneratorLike [TC <: AbstractId] (ev: PräfixLike [TC], x: {def Anwenden (s: String): TC}) { def generate(): TC = x.apply (ev.prefix + " - "+ UUID.randomUUID()) }' Was denkst du über so etwas? – kpbochenek

0

Strukturelle Typisierung ist keine besonders gute Idee, auf der JVM, also immer versuchen, die def test(x: {def apply(s: String)}): TC Typen Sachen zu vermeiden, weil es realisiert wird Reflexion, die weise ein Hund Leistung sein kann.

Zweitens sollten Sie wahrscheinlich vermeiden, val in einem trait zu verwenden. Lesen Sie here.

Der Ansatz, den Sie in Betracht gezogen haben, ist tatsächlich der richtige, und zwar Typklassen.

trait HasGenerator[T] { 
    def apply(uuid: String): T 
    def generate[T : Generator] = apply(Generator[T].generate) 
} 

final case class ChairId(id: String) 

object ChairId extends HasGenerator[ChairId] 

trait Generator[TO] { 
    def prefix: String 
    def generate(): String = prefix + "-" + UUID.randomUUID() 
    def apply(): String = generate 
} 

object Generator { 
    def apply[T : Generator] = implicitly[Generator[T]] 
} 

// Notice .type is not necessary 
implicit object ChairIdGenerator extends Generator[ChairId] { 
    override def prefix = "CHAIR" 
} 

Warum nicht einfach verwenden:

ChairId(Generator[ChairId]) 

wie viel des Guten Das alles scheint aber so kann man ganz irgendwie leicht. Es lohnt sich, Ihre Anforderungen noch etwas genauer zu formulieren, da Typenklassen noch nicht wirklich notwendig erscheinen. Sie könnten nur tun mit:

aktualisieren

Wenn Sie so etwas wie die HasGenerator verwenden, die ich oben mit dem Begleitobjekt in Verbindung hinzugefügt haben, können Sie jetzt erfolgreich ChairId.generate()

+0

Diese mag ich nicht, weil Sie leicht mit etwas wie: 'ChairId (implizit [Generator [TableId]]())' Ich habe Klassen/Objekte im Produktionscode verwendet und einfach nur nett hinzufügen möchten Schnittstelle zum Schreiben von Tests. Eine der Sachen, die ich würde, würde sein, Möglichkeit hinzuzufügen, IDs zu erzeugen und Präfix mit passender Fallklasse fest zu koppeln. Da wir den Großteil unseres Zeitcodes gelesen haben, wollte ich wirklich eine Lösung bereitstellen, in der wir 'ChairId.generate()' oder 'TableId.generate()' schreiben können – kpbochenek

+0

Hi @ kpbochenek Ich würde dann einen ganz anderen Ansatz vorschlagen. Sehen Sie hier: https://github.com/outworkers/phantom/blob/develop/phantom-dsl/src/test/scala/com/websudos/phantom/tables/package.scala#L41 – flavian

+0

https: // github .com/outworkers/util # data-sampling – flavian