views:

903

answers:

3

Is there something I've got wrong with the following fragment:-

object Imp {
  implicit def string2Int(s: String): Int = s.toInt

  def f(i: Int) = i

  def main(args: Array[String]) {
    val n: Int = f("666")
  }
}

I get the following from the 2.8 compiler:-

Information:Compilation completed with 1 error and 0 warnings
Information:1 error
Information:0 warnings
...\scala-2.8-tests\src\Imp.scala
Error:Error:line (4)error: type mismatch;
found : String
required: ?{val toInt: ?}
Note that implicit conversions are not applicable because they are ambiguous:
both method string2Int in object Imp of type (s: String)Int
and method augmentString in object Predef of type (x:String)scala.collection.immutable.StringOps
are possible conversion functions from String to ?{val toInt: ?}
implicit def string2Int(s: String): Int = s.toInt

+1  A: 

There is already an implicit conversion in scope, from scala.Predef. You don't need to declare your own implicit conversion to add a toInt method to a String. You have 3 options (I'd go for the last one!):

  • Change your method name to something like asInt
  • Unimport the conversion in Predef
  • Don't bother defining your own and use instead the toInt that comes bundled with the scala library

Note that scala will only make use of an in-scope implicit conversion if it is unique.

oxbow_lakes
Thanks for the quick reply, unfortunately the implicit from Predef doesn't kick-in if I comment out my implicit above, it only appears to be in scope when the competition turns up ;-) The toInt option is fine but I was exploring a series of conversions from String in a method determined by a type parameter, one of them being String i.e. a no-op.
Don Mackenzie
I'll have to try this tomorrow with 2.8. I use the `toInt` conversion in production code (i.e. using 2.7) all the time!
oxbow_lakes
+5  A: 

What is happening is that Java does not define a toInt method on String. In Scala, what defines that method is the class StringOps (Scala 2.8) or RichString (Scala 2.7).

On the other hand, there is a method toInt available on Int as well (through another implicit, perhaps?), so the compiler doesn't know if it is to convert the string to StringOps, through the defined implicit, or to Int, through your own implicit.

To solve it, call the implicit explicitly.

object Imp {
  implicit def string2Int(s: String): Int = augmentString(s).toInt

  def f(i: Int) = i

  def main(args: Array[String]) {
    val n: Int = f("666")
  }
}
Daniel
Thanks for the help, I hadn't realised RichString has been superceded. What I'm still confused by is that when I don't provide an implicit, the conversion via augmentString isn't found and a type error occurs. I'm trying not to use an explicit call because the type to convert to is provided by a type parameter in the actual method I'm trying to write.
Don Mackenzie
RichString hasn't been superceded, it still exists. I don't know what is the deal with AugmentedString though. The problem is that the compiler doesn't know which ".toInt" method to call: the one on AugmentedString or the one on Int.
Daniel
RichString still exists? I better get this delete key checked out.
extempore
@extempore It still exists in Scala 2.7, as indicated. I'm pretty sure. :-)
Daniel
A: 

I think I have a workaround.

If I create a RichString from the String argument, the implicit conversion occurs from RichString to Int using the implicit method I provide (this works for 2.7.x and 2.8). If I remove my implicit I get a type error.

object Imp {

  implicit def string2Int(rs: RichString): Int = rs.toInt

  def f(i: Int) = i

  def main(args: Array[String]) {
    val n: Int = f(new RichString("666"))

    println(n)
  }
}

I'm still confused as to why both implicits came into scope and clashed when I provided an implicit and as to why the Predef one didn't come into scope when I didn't provide one for String to Int. I suppose the question about an implicit conversion from String to Int remains open.

Don Mackenzie