【问题标题】:Exploit type constraints of case classes in Scala match expression在 Scala 匹配表达式中利用案例类的类型约束
【发布时间】:2019-07-01 14:14:42
【问题描述】:

我正在构建一个为给定 Config[T] 生成 Product[T] 实例的工厂。有两种类型的配置:

trait Config[T]
case class SimpleConfig[T]() extends Config[T]
case class SpecialConfig[T <: Special]() extends Config[T]

工厂只是在配置上匹配并委托给两个子工厂:

def factory[T](config: Config[T]): Product[T] = config match {
  case c: SimpleConfig[_] => buildA[T](c)
  case sc: SpecialConfig[_] => buildASpecial[T](sc)
}

def buildA[X](c: Config[X]): Product[X] = ???
def buildASpecial[X <: Special](sc: SpecialConfig[X]): Product[X] = ???

但是,这不会编译,因为编译器无法验证传递给buildASpecial() 的配置的类型参数是Special 的子类型。看起来它可以实际验证参数 scSpecialConfig[X]X &lt;: Special 但它没有看到 X 实际上等于 T 所以它可以得出结论 T 也是Special.

也许我遗漏了一些东西,但上面的调整对我来说似乎是有效的。是否可以在没有显式转换的情况下解决此问题?

【问题讨论】:

    标签: scala types pattern-matching


    【解决方案1】:

    假设ProductConfig 是协变的,您可以执行以下操作:

    trait Special
    trait Product[+T]
    
    trait Config[+T]
    case class SimpleConfig[T]() extends Config[T]
    case class SpecialConfig[T <: Special]() extends Config[T]
    
    
    def buildA[X](c: Config[X]): Product[X] = ???
    def buildASpecial[X <: Special](sc: SpecialConfig[X]): Product[X] = ???
    
    def factory[T](config: Config[T]): Product[T] = config match {
      case c: SimpleConfig[T] => buildA[T](c)
      case sc: SpecialConfig[t] => buildASpecial[t](sc)
    }
    

    否则,T &lt;: Special 的子类型问题似乎无法通过模式匹配轻松解决,必须使用良好的旧子类多态性来处理(您选择使用 &lt;: 在第一名):

    trait Special
    trait Product[T]
    
    trait Config[T] {
      def buildProduct: Product[T]
    }
    case class SimpleConfig[T]() extends Config[T] {
      def buildProduct = buildA[T](this)
    }
    case class SpecialConfig[T <: Special]() extends Config[T] {
      def buildProduct = buildASpecial[T](this)
    }
    
    
    def buildA[X](c: Config[X]): Product[X] = ???
    def buildASpecial[X <: Special](sc: SpecialConfig[X]): Product[X] = ???
    
    def factory[T](config: Config[T]): Product[T] = config.buildProduct
    

    【讨论】:

    • 奇怪的是,编译器可以在协变情况下计算出t &lt;: T,但在更简单的不变情况下却无法计算出t =:= T
    • @AlexeyRomanov 如果它将tT 统一起来,那么对这个简单的代码sn-p 进行类型检查将再次需要对流敏感的分析,其中Nothing &lt;: T &lt;: Any 在方法入口处是已知的,但随后不知何故在第二个match-case-branch 中更改为Nothing &lt;: T &lt;: Special。据我所知,打字机根本不做任何对流敏感的类型分析,这会导致这种“奇怪”的情况。我不知道为什么它不这样做,我认为它只是 complex (如 - way 这个简单示例所暗示的更复杂)。我希望看到更好的解决方法。
    • 这是允许case sc: SpecialConfig[t] =&gt; buildASpecial[T](sc)编译所必需的,但我认为case sc: SpecialConfig[t] =&gt; buildASpecial[t](sc)不一定需要它:t &lt;: Specialt ~ T可以分开使用。
    • 非常感谢!不幸的是,ProductT 中是不变的,所以我不得不求助于第二种方法。但是,无论如何,这对我来说似乎很合理。
    猜你喜欢
    • 1970-01-01
    • 2017-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-04
    • 2017-04-25
    • 2014-08-12
    相关资源
    最近更新 更多