【问题标题】:Scala generic type and implicit type classScala 泛型类型和隐式类型类
【发布时间】:2018-11-21 21:02:48
【问题描述】:

我正在尝试了解 scala 隐式及其在类型类中的用法。 我有一个通用特征 FromString 和一个伴随对象,它为标准类型定义了 FromString 的隐式实例,如下所示:

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)
}

现在我可以使用 FromString 对象的 convertFromString 函数将字符串转换为标准类型,如下所示。这运行正确。

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");
    }
}

但是,当我尝试使用泛型类中的相同内容时,如下所示,它会导致错误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");
    }
}

隐式在对象中定义,我猜它们在范围内也可用。

【问题讨论】:

    标签: scala typeclass implicit


    【解决方案1】:

    具体类型Int仅在行中知道

    val width = new Knob[Int]("Width",  3);
    

    在此行之前,编译器不知道T 的类型是什么,并且它无法在任何地方找到IntFromString 作为隐式参数并将其插入。因此,您必须将IntFromStringmain 中的调用站点传递到Knob.update 中的使用站点。为此,只需将FromString 类型类添加到T

    class Knob[T: FromString](val name: String, default: T) {
      ...
    }
    

    使用泛型类型和类型类进行编程时的一般规则:具体类型类实例必须“在世界尽头”插入,在泛型类型参数(如T)固定并成为具体类型的行中(比如Int)。您必须将这些类型类实例从可以确定具体类型的站点一直传递到使用类型类实例的通用代码。


    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)
    }
    
    import FromString._
    
    class Knob[T: FromString](val name: String, default: T) {
        var value: T = default
    
        def update(valstr: String) {
            value = convertFromString[T](valstr)
        }
    }
    
    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");
    
    
            val width = new Knob[Int]("Width",  3)
            width.update("100")
            println(s"width=$width")
        }
    }
    

    【讨论】:

    • 我不想将T 绑定为FronString 类型(这会使Knob.value 成为FromString 类型,我不想要),事实上@ 987654337@ 可以是任何混凝土类型。如果存在这样的对象,我希望编译器在更新函数中隐式提供相应的 FromString 对象。
    • @Sudhanshu "bind T to be of type FromString" 是什么意思? Scala 中没有类型宇宙,也没有类型分层。 T 不能是 FromString”类型的。它只能需要类型类FromString。如果您不需要类型类,那么您不能使用convert(不应该是convertFromString 吗?)。您的Knob.valueT 类型。添加FromString 类型类不会更改Knob.value 的类型。
    • 我还在学习 scala,有点忘记了上下文边界。我明白你的意思。完成您建议的更改后,我收到错误 could not find implicit value for evidence parameter of type util.FromString[T]
    • @Sudhanshu 我已经添加了完整的代码示例。编译和运行都很好,没有任何错误。您收到的错误消息可能是由导入的一些小问题引起的。
    • 进口似乎是问题所在。将尝试解决它们。谢谢
    猜你喜欢
    • 2013-11-15
    • 1970-01-01
    • 2012-11-01
    • 2020-10-12
    • 2020-02-11
    • 2015-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多