2009-08-27 10 views
15

Ich versuche, eine implizite Konvertierung von jeder Art (zum Beispiel Int) in einen String zu erstellen ...Vermeidung implizite def Mehrdeutigkeit in Scala

Eine implizite Umwandlung in String bedeutet RichString Methoden (wie umgekehrt) sind nicht verfügbar .

implicit def intToString(i: Int) = String.valueOf(i) 
100.toCharArray // => Array[Char] = Array(1, 0, 0) 
100.reverse // => error: value reverse is not a member of Int 
100.length // => 3 

eine implizite Konvertierung zu RichString bedeutet String Methoden (wie ToCharArray)

implicit def intToRichString(i: Int) = new RichString(String.valueOf(i)) 
100.reverse // => "001" 
100.toCharArray // => error: value toCharArray is not a member of Int 
100.length // => 3 

Verwendung sowohl implizite Konvertierungen bedeutet duplizierten Methoden (wie die Länge) nicht verfügbar sind, ist nicht eindeutig.

implicit def intToString(i: Int) = String.valueOf(i) 
implicit def intToRichString(i: Int) = new RichString(String.valueOf(i)) 
100.toCharArray // => Array[Char] = Array(1, 0, 0) 
100.reverse // => "001" 
100.length // => both method intToString in object $iw of type 
    // (Int)java.lang.String and method intToRichString in object 
    // $iw of type (Int)scala.runtime.RichString are possible 
    // conversion functions from Int to ?{val length: ?} 

So ist es möglich, zu String implizit zu konvertieren und noch alle Streich- und RichString Methoden unterstützen?

Antwort

2

Entweder eine riesige Proxy-Klasse machen, oder saugen sie auf und erfordern die Client es eindeutig zu machen:

100.asInstanceOf [Zeichenfolge] .length

+0

Es scheint, dass "suck it up" die beste Option ist. API-Design in Scala ist wesentlich schwieriger als ein API-Consumer. Ich wollte einen Datentyp erstellen, der einen Wert umschließt und dem API-Benutzer ermöglicht, den Datentyp so zu behandeln, als wäre es der Typ des internierten Werts. Z.B. Wenn es ein Int umschließt, behandle das Objekt wie ein int. Stellt sich heraus, das ist zu schwierig. Ich bin angesichts meiner Datentyp Hilfsmethoden geben: Typ T val internedValue: T def s = internedValue.asInstanceOf [Zeichenfolge] i def = internedValue.asInstanceOf [Int] ... etc – Synesso

+20

Bevorzugen '(100: String) .length', da dies typsicher ist, 'asInstanceOf' hingegen nicht. Außerdem ist es hübscher und kürzer. –

+2

Nicht nur das, aber asInstanceOf wird hier nie funktionieren, da es immer nur Downcasting (was hier scheitern muss) und keine implizite Konvertierung starten wird. Mit anderen Worten, '100.asInstanceOf [String] .length' wird immer mit einer' ClassCastException' fehlschlagen. –

2

Die einzige Option, die ich sehe, besteht darin, eine neue String Wrapper-Klasse MyString zu erstellen und diese Methode aufzurufen, die im mehrdeutigen Fall aufgerufen werden soll. Dann könnten Sie implizite Konvertierungen zu MyString und zwei implizite Konvertierungen von MyString zu String und RichString definieren, für den Fall, dass Sie sie an eine Bibliotheksfunktion übergeben müssen.

+0

Das klingt nach einem fairen Ansatz, aber eine Menge Arbeit. Außerdem vermute ich, dass es nicht zukunftssicher ist, da andere implizite Konvertierungen eingeführt werden. – Synesso

+0

Sicher ist es eine Menge Arbeit, aber nicht viel Denken, nur tippen. ;) Und warum wäre es nicht zukunftssicher? Niemand zwingt Sie, implizite Conversions zu verwenden, die in der Zukunft eingeführt werden könnten, nicht einmal die im Preload. –

+0

@Kim Stebel: 'Sicher, es ist eine Menge Arbeit, aber nicht viel Denken, nur tippen 'Programmierer sind faule Denker, nicht hart arbeitende Schreibkräfte. –

1

Ich bin verwirrt: Können Sie nicht .toString auf jeden beliebigen Typ verwenden und so die Notwendigkeit für implizite Konvertierungen vermeiden?

+0

Ich denke nicht, dass das die Dinge ändern würde, außer vielleicht Nullzeigerausnahmen einzuführen. Um klar zu sein, schlagen Sie vor, String.valueOf (i) zu i.toString zu ändern? – Synesso

5

Ich habe keine Lösung haben, sondern Kommentar dass der Grund RichString Methoden sind nicht verfügbar nach Ihrer intToString implizit ist, dass Scala implizite Aufrufe nicht ketten (siehe 21.2 "Regeln für implicits" in Programmierung in Scala).

Wenn Sie ein Intermediate String einführen, wird Scala die implict-Konvertierung zu einem RichString machen (das implizit in Predef.scala definiert ist).

Z. B.

$ scala 
Welcome to Scala version 2.7.5.final [...]. 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> implicit def intToString(i: Int) = String.valueOf(i) 
intToString: (Int)java.lang.String 

scala> val i = 100 
i: Int = 100 

scala> val s: String = i 
s: String = 100 

scala> s.reverse 
res1: scala.runtime.RichString = 001 
+0

Das ist gut zu wissen! Vielen Dank. – Synesso

+0

Ist dies "nur" ein Nachteil, zirkuläre implizite konvertierbare Beziehungen zuzulassen, oder gibt es andere Gründe? Selbst wenn Sie einen Kreis haben, sollten Sie statisch die "nächste" Implementierung finden. Ein Diagramm zu erkunden, ohne Graphen zweimal zu besuchen, ist nicht so schwer, obwohl es eine Weile dauern könnte. – Raphael

5

Ab Scala 2.8, dies wurde verbessert. Gemäß this paperVermeidung von Mehrdeutigkeiten):

Zuvor die spezi fi schen überladener Methode oder impliziter Konvertierung ausschließlich auf Basis gewählt wird, würde auf der Argumenttyp Methode. Es gab eine zusätzliche Klausel , die besagte, dass das spezifischste Verfahren nicht in einer geeigneten Oberklasse irgendeiner der anderen Alternativen definiert werden könne. Dieses Schema wurde in Scala 2.8 durch das folgende, liberalere ersetzt: Wenn zwei verschiedene anwendbare Alternativen einer überladenen Methode oder eines impliziten verglichen werden, erhält jede Methode einen Punkt für mehr spezifische Argumente und einen anderen Punkt für in einer geeigneten Unterklasse definiert werden. Eine Alternative "gewinnt" gegenüber einer anderen, wenn sie in diesen beiden Vergleichen eine größere Anzahl Punkte erhält.Dies bedeutet insbesondere, dass, wenn Alternativen identische Argumenttypen haben, derjenige, der in einer Unterklasse de fi niert ist, gewinnt.

Siehe that other paper (§ 6.5) für ein Beispiel.

+0

Ihr Link zum ersten Papier ist kaputt. –

2

Die akzeptierte Lösung (geschrieben von Mitch Blevins) wird nie funktionieren: Downcasting Int zu String mit asInstanceOf wird immer fehlschlagen.

Eine Lösung für Ihr Problem ist es, eine Umwandlung von jedem String-convertible Typ RichString (oder besser gesagt, zu StringOps wie es jetzt genannt wird) hinzuzufügen:

implicit def stringLikeToRichString[T](x: T)(implicit conv: T => String) = new collection.immutable.StringOps(conv(x)) 

Dann Ihre Conversion definieren (n) Zeichenfolge wie zuvor:

scala> implicit def intToString(i: Int) = String.valueOf(i) 
warning: there was one feature warning; re-run with -feature for details 
intToString: (i: Int)String 

scala> 100.toCharArray 
res0: Array[Char] = Array(1, 0, 0) 

scala> 100.reverse 
res1: String = 001 

scala> 100.length 
res2: Int = 3