Could someone give a quick explanation why implicit conversion doesn't work in these cases? Thanks.
scala> implicit def strTwoInt (s: String):Int = s.toCharArray.map{_.asDigit}.sum
strTwoInt: (s: String)Int
scala> List[Int]("1","2","3") sum
res3: Int = 6
scala> List("1","2","3") sum
<console>:9: error: could not find implicit value for parameter num: Numeric[java.lang.String]
List("1","2","3") sum
scala> val a = List("1","2","3")
scala> a.foldLeft(0)((i:Int, j:Int) => i+j)
<console>:10: error: type mismatch;
found : (Int, Int) => Int
required: (Int, java.lang.String) => Int
Your implicit conversion converts a String in an Int. In your first example, it is triggered by the fact that you try to put Strings into a List of Ints.
In your second example, you have a List of String and you call the method sum, which takes an implicit Numeric[String]
. Your conversion does not apply because you neither try to pass a String somewhere the compiler was expecting an Int, nor you tried to call a method which is defined in Int and not in String. In that case, you can either define a Numeric[String]
which use explicitly your conversion, or use a method which takes a List[Int]
as parameter (giving hint to the compiler):
scala> def sumIt( xs: List[Int] ) = xs sum
sumIt: (xs: List[Int])Int
scala> sumIt( List("1","2","3") )
res5: Int = 6
In the third example, the foldLeft second argument must be of type:
(Int,String) => Int
the one you actually passed is of type:
(Int,Int) => Int
however, you did not define any implicit conversion between these two types. But:
a.foldLeft(0)((i:Int, j:String) => i+j)
triggers your conversion and works.
Edit: Here's how to implement the Numeric[String]
:
implicit object StringNumeric extends math.Numeric[String] {
val num = math.Numeric.IntIsIntegral
def plus(x: String, y: String) = num.plus(x,y).toString
def minus(x: String, y: String) = num.minus(x,y).toString
def times(x: String, y: String) = num.times(x,y).toString
def negate(x: String): String = num.negate(x).toString
def fromInt(x: Int) = x.toString
def toInt(x: String) = x
def toLong(x: String) = toInt(x)
def toFloat(x: String) = toInt(x)
def toDouble(x: String) = toInt(x)
def compare(x:String,y:String) = num.compare(x,y)
}
scala> List("1","2","3") sum
res1: java.lang.String = 6
It works, but the result is a String.
Here's a quick explanation: implicit conversions apply to the types the convert from and to directly (e.g., String
and Int
in your case), and not to any parametrized type T[String]
or T[Int]
— unless implicit conversions have been defined for T
itself, and it is not the case for lists.
Your implicit conversion does not apply in your two cases (and even if you had an implicit conversion from List[String]
to List[Int]
, it wouldn't apply). It would automatically applied only when you need a value of type Int
and you're passing String
instead. Here, in the first case, the method sum
asks for a Numeric[String]
implicit parameter — an implicit conversion from String
to Int
does not come into play here.
Similar problem for your next attempt: the foldLeft
on your collection requires a function of type (Int, String) => Int
. Imagine the mess we would get into if, based on an implicit conversion from String
to Int
, the compiler automatically provided an implicit conversion from (Int, Int) => Int
to (Int, String) => Int
…
For all these cases, the easy way to fix it is to explicitly call .map(stringToInt)
on your collection beforehand.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With