I am trying to understand scala implicits and their usage in type classes. I have a generic trait FromString and a companion object defining implicit instances of FromString for standard types as specified below:
file: /src/main/scala/util/FromString.scala
package util
trait FromString[A] {
def fromString(string: String): A
}
object FromString {
def toFromString[T](p: String => T): FromString[T] = new FromString[T] {
def fromString(x: String): T = p(x);
}
implicit val IntFromString = toFromString[Int](_.toInt);
implicit val ByteFromString = toFromString[Byte](_.toByte);
implicit val LongFromString = toFromString[Long](_.toLong);
implicit val ShortFromString = toFromString[Short](_.toShort);
implicit val FloatFromString = toFromString[Float](_.toFloat);
implicit val DoubleFromString = toFromString[Double](_.toDouble);
implicit val BooleanFromString = toFromString[Boolean](_.toBoolean);
implicit val IntListFromString = toFromString[List[Int]](_.split(',').map(_.toInt).toList);
def convertFromString[A](string: String)(implicit e: FromString[A]): A = e.fromString(string)
}
Now I can use the convertFromString function of the FromString object to convert string to standard types as shown below. This runs correctly.
file: /src/main/scala/top/Main1.scala
package top
import util.FromString._
object Main {
def main(args: Array[String]): Unit = {
val d = convertFromString[Double]("4.5");
val i = convertFromString[Int]("42");
val li = convertFromString[List[Int]]("1,2,3");
println(s"d=$d i=$i li=$li");
}
}
However, when I try to use the same thing from a generic class, as shown below, it results in an error could not find implicit value for parameter e: util.FromString[T]
file: /src/main/scala/util/Knob.scala
package util
import FromString._
class Knob[T](val name: String, default: T){
var value: T = default;
def update(valstr: String) {
value = convertFromString[T](valstr);
}
}
file: /src/main/scala/top/Main2.scala
package top
import util.Knob
import util.FromString._
object Main {
def main(args: Array[String]): Unit = {
val width = new Knob[Int]("Width", 3);
width.update("100");
println(s"width=$width");
}
}
The implicits are defined in the object and I am guessing they are also available in the scope.