2009-08-27 24 views
15

Estoy tratando de crear una conversión implícita de cualquier tipo (por ejemplo, Int) a una cadena ...Evitar la ambigüedad definición implícita en Scala

una conversión implícita de cadena significa RichString métodos (como el inverso) no están disponibles .

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 

una conversión implícita a RichString significa métodos de String (como toCharArray) no están disponibles

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 

Utilizando ambas conversiones implícitas significa métodos duplicados (como longitud) son ambiguas.

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: ?} 

Por lo tanto, es posible convertir implícitamente a cuerdas y todavía apoyar a todos los métodos de String y RichString?

Respuesta

2

O hacer una gran clase de proxy, o que aguantar y pedir al cliente para eliminar la ambigüedad que:

100.asInstanceOf [String] .length

+0

Parece que 'chuparlo' es la mejor opción. El diseño de API en Scala es significativamente más difícil que ser un consumidor de API. Quería crear un tipo de datos que ajuste un valor y permita que el usuario de API trate el tipo de datos como si fuera el tipo del valor interno. P.ej. si envuelve un Int, entonces trate el objeto como un int. Resulta que esto es muy difícil. estoy considerando dar mis tipos de datos métodos auxiliares: tipo T val internedValue: T def s = internedValue.asInstanceOf [String] def i = internedValue.asInstanceOf [Int] etc ... – Synesso

+20

Prefiero '(100: String) .length', ya que es seguro, mientras que 'asInstanceOf' no lo es. Además, es más bonita y más corta. –

+2

No solo eso, sino queInstanceOf nunca funcionará aquí, ya que solo lo hace downcasting (que está destinado a fallar aquí) y no iniciará ninguna conversión implícita. En otras palabras, '100.asInstanceOf [String] .length' siempre fallará con' ClassCastException' –

2

La única opción que veo es crear un nuevo String Wrapper clase MyString y dejar que invoque el método que desee que se llame en el caso ambiguo. Luego, podría definir conversiones implícitas en MyString y dos conversiones implícitas desde MyString a String y RichString, por si acaso necesita pasarlas a una función de biblioteca.

+0

Esto suena como un enfoque justo, pero mucho trabajo. Además, sospecho que no es una prueba de futuro, ya que se introducen otras conversiones implícitas. – Synesso

+0

Claro que es mucho trabajo, pero no mucho pensar, solo escribir. ;) ¿Y por qué no sería a prueba de futuro? Nadie te obliga a utilizar conversiones implícitas que puedan introducirse en el futuro, ni siquiera aquellas en la precarga. –

+0

@Kim Stebel: 'Claro que es mucho trabajo, pero no mucho pensar, solo los programadores de tipeo son pensadores perezosos, no mecanógrafos que trabajan duro. –

1

Estoy confundido: ¿no puede usar .toString en cualquier tipo de manera que se evite la necesidad de conversiones implícitas?

+0

No creo que eso cambie las cosas, excepto tal vez para introducir excepciones de puntero nulo. Para que quede claro, usted está proponiendo cambiar String.valueOf (i) a i.toString? – Synesso

5

no tengo una solución, pero comentarán que los métodos RichString de razón no están disponibles después de que su intToString implícito es que Scala no encadena llamadas implícitas (consulte 21.2 "Reglas para implicaciones" en Programación en Scala).

Si introduce un intermedio String, Scala hará la conversión implícita a un RichString (que implícita se define en Predef.scala).

por ejemplo,

$ 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

¡Es bueno saberlo! Gracias. – Synesso

+0

¿Es esto "solo" un inconveniente permitir relaciones circulares implícitas convertibles, o hay otras razones? Dado que incluso si tiene un círculo, debe encontrar estáticamente la implementación "más cercana". Explorar un gráfico sin visitar los gráficos dos veces no es tan difícil, aunque podría llevar un tiempo. – Raphael

5

Como de Scala 2,8, esto ha sido mejorada. Según this paper (§ Evitar ambigüedades ):

Anteriormente, la ficción más especí co método sobrecargado o conversión implícita sería elegido basándose únicamente en los tipos de argumentos del método. Había una cláusula adicional que decía que el método más específico no podía ser definido en una superclase adecuada de cualquiera de las otras alternativas. Este esquema ha sido reemplazado en Scala 2.8 por el siguiente, más liberal: Al comparar dos alternativas aplicables diferentes de un método sobrecargado o de un método implícito, cada método obtiene un punto por tener más argumentos específicos de , y otro punto por se define en una subclase adecuada . Una alternativa "gana" sobre otra si obtiene un mayor número de puntos en estas dos comparaciones.Esto significa en particular que si las alternativas tienen tipos de argumentos idénticos, se gana la que se define en una subclase .

Ver that other paper (§6.5) para un ejemplo.

+0

Su enlace al primer documento está roto. –

2

La solución aceptada (publicada por Mitch Blevins) nunca funcionará: downcasting Int a String usando asInstanceOf siempre fallará.

Una solución a su problema es añadir una conversión de cualquier tipo String-convertible en RichString (o más bien, a StringOps como se llama ahora):

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

A continuación, defina su conversión (s) a cadena como antes:

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