scalaimplicit-conversion

Implicit evidence for string to double


Let's say I want to divide anything implicitly convertible to Double:

def divide[T](a: T, b: T)(implicit ev: T => Double) = a / b
divide(1, 2)
// 0.5

However, (1) this wouldn't work with Strings out of box, and (2) attempt to provide implicit conversion like:

implicit def string2double(a: String): Double = a.toDouble

will fail:

cell3.sc:3: type mismatch;
 found   : a.type (with underlying type String)
 required: ?{def toDouble: ?}
Note that implicit conversions are not applicable because they are ambiguous:
 both method augmentString in object Predef of type (x: String): scala.collection.StringOps
 and method string2double in class Helper of type (a: String): Double
 are possible conversion functions from a.type to ?{def toDouble: ?}
implicit def string2double(a: String): Double = a.toDouble
                                                ^
cell3.sc:3: value toDouble is not a member of String
implicit def string2double(a: String): Double = a.toDouble
                                                  ^
Compilation Failed

Why readily available conversion is not used and how to generalize T to strings as well?


Solution

  • Of course you know best for your specific use case, but I think it's worth pointing out that using implicit conversions can have unexpected pitfalls, especially when used on types as widely used as Double and String: https://www.scala-lang.org/api/2.13.12/scala/language$.html#implicitConversions:languageFeature.implicitConversions

    Note for example that toDouble is not really defined on String, but rather on an implicit class which is wrapped around java.lang.String through an implicit conversion: https://www.scala-lang.org/api/2.13.12/scala/collection/StringOps.html#toDouble:Double

    If you still think that using an implicit conversion is the best course of action for your use case, you can explicitly wrap the String in the StringOps wrapper to avoid the compiler some confusion. The following works:

    def divide[T](a: T, b: T)(implicit ev: T => Double) = a / b
    
    import scala.language.implicitConversions
    implicit def string2double(a: String): Double = augmentString(a).toDouble
    
    "1" / "2"
    

    You can play around with this code here on Scastie.

    As for the reason why your attempt doesn't work, my best guess (but take it with a grain of salt and validate it with someone who actually knows the Scala compiler) is that the compiler does an implicit conversion pass, during which implicit conversions are not yet available to the code, making "recursive implicit conversions" impossible.