【问题标题】:Casting "hollow" higher kinded type values to avoid instantiations铸造“空心”更高种类的类型值以避免实例化
【发布时间】:2013-01-18 11:01:31
【问题描述】:

我发现自己看了一些 Scalawags#2 的录音,然后是关于类型擦除的部分,Dick Wall 指出反射最终会咬你的脚。

所以我在考虑我经常做的事情(我也看到了 Scala Collections 的实现)。假设我有一个带有序列化程序的系统,将系统作为类型参数:

trait Sys[S <: Sys[S]] { type Tx }
trait FooSys extends Sys[FooSys]

trait Serializer[S <: Sys[S], A] {
  def read(implicit tx: S#Tx): A
}

现在有很多类型A 可以构造序列化器没有值参数,所以本质上系统类型参数是“空心的”。由于在我的示例中大量调用了序列化程序,因此我正在保存实例化:

object Test {
  def serializer[S <: Sys[S]] : Serializer[S, Test[S]] =
    anySer.asInstanceOf[Ser[S]]

  private val anySer = new Ser[FooSys]

  private final class Ser[S <: Sys[S]] extends Serializer[S, Test[S]] {
    def read(implicit tx: S#Tx) = new Test[S] {} // (shortened for the example)
  }
}
trait Test[S <: Sys[S]]

知道这是正确的,但当然,asInstanceOf 有难闻的气味。 对这种方法有什么建议吗?让我补充两点

  • 将类型参数从 trait Serializer 的构造函数移动到 read 方法不是一种选择(有特定的序列化程序需要在 S 中参数化值参数)
  • Serializer 的类型构造函数参数添加方差不是一个选项

【问题讨论】:

    标签: scala casting type-erasure higher-kinded-types


    【解决方案1】:

    简介:

    我对你的例子有点困惑,我可能误解了你的问题,我觉得STx 之间存在某种类型的递归,我没有从你的问题中得到(因为如果不是, S#Tx 可以是任何东西,我不明白anySer 的问题)

    暂定答案:

    在编译时,对于Ser[T] 的任何实例,都会有一个明确定义的类型参数T,因为您想在实例化时保存它,所以对于给定的特定类型,您将有一个anySer Ser[T] A

    您在某种程度上说的是Ser[A] 将作为Ser[S] 用于任何S。根据 A 型和 S 型的关系,这可以用两种方式来解释。

    1. 如果每个A&lt;:&lt;S 都可以进行这种转换,那么您的序列化程序是COVARIANT,您可以将您的anySer 初始化为Ser[Nothing]。由于 Nothing 是 Scala 中每个类的子类,因此您的 anySer 将始终作为 Ser[Whatever]

    2. 如果每个S&lt;:&lt;A 都可以进行这种转换,那么您的序列化程序是CONTRAVARIANT,您可以将您的anySer 初始化为Ser[Any]。由于 Any 是 Scala 中每个类的子类,因此您的 anySer 将始终作为 Ser[Whatever]

    3. 如果不是前一种情况,则表示:

      def serializer[S <: Sys[S]] : Serializer[S, Test[S]] =
      anySer.asInstanceOf[Ser[S]]
      

      可能会在运行时产生可怕的失败,因为会有一些 S 序列化程序无法工作。如果没有可能发生这种情况的 S,那么您的班级属于 1 或

    评论后编辑

    如果您的类型确实是不变的,则通过强制转换进行的转换会破坏不变关系。您基本上是在强制类型系统执行非自然转换,因为您知道基于您自己对您编写的代码的了解不会发生任何错误。如果是这种情况,那么强制转换是正确的方法:您正在强制使用与编译器可以正式检查的类型不同的类型,并且您正在明确这一点。我什至会发表一个很大的评论,说为什么你知道操作是合法的并且编译器无法猜测并最终附加一个漂亮的单元测试来验证“非正式”关系是否始终成立。

    总的来说,我认为应该非常小心地使用这种做法。强类型语言的好处之一是编译器执行正式的类型检查,帮助您发现早期错误。如果你故意破坏它,你就放弃了这个巨大的好处。

    【讨论】:

    • 在实际实现中,S#Tx当然会有一个有用的上限。否则,您的解释是正确的,“Ser[A] 将作为Ser[S] 用于任何S”,此外显然还有S &lt;: Sys[S]]。所以案例#2成立。但是因为Sys中的S是不变的,所以Serializer中的S也一定是不变的,所以我不能返回Ser[Any]
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-08-12
    • 1970-01-01
    • 2015-12-09
    • 2020-12-29
    • 2018-06-03
    • 2020-12-29
    • 2019-01-08
    相关资源
    最近更新 更多