14
votes

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

3

3 Answers

24
votes

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")
  }
}
8
votes

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.

1
votes

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.