【问题标题】:Compile time check on some property对某些属性进行编译时检查
【发布时间】:2015-06-10 17:28:07
【问题描述】:

给定以下 scala 代码:

sealed trait Color
case object Red extends Color
case object Blue extends Color

sealed trait Car {
  def isBroken: Boolean
  def color: Color
}

如何定义这样的方法:

def fixBrokenRedCar(c: A): B

也就是说,AB 应该是什么?该方法应该只接受同时是RedisBroken = true 的汽车。否则它应该发出编译错误。此外,输出 B 应该包含有关其类型的信息,这样如果我创建另一个方法 destroyRedCar(c: B) 并将其应用于输出,它应该会相应地编译。

【问题讨论】:

    标签: scala types


    【解决方案1】:

    然后您应该将数据移动到类型级别:

    trait Bool
    trait T extends Bool
    trait F extends Bool
    
    trait Color
    trait Red extends Color
    trait Blue extends Color
    
    trait Car[Clr <: Color, Brkn <: Bool]
    
    def fixBrokenCar[Cr <: Car[Red, T]](c: Cr) = new Car[Red, F]{}
    
    scala> fixBrokenCar(new Car[Blue, T]{})
    <console>:16: error: inferred type arguments [Car[Blue,T]] do not conform to method fixBrokenCar's type parameter bounds [Cr <: Car[Red,T]]
                  fixBrokenCar(new Car[Blue, T]{})
                  ^
    <console>:16: error: type mismatch;
     found   : Car[Blue,T]
     required: Cr
                  fixBrokenCar(new Car[Blue, T]{})
                               ^
    
    scala> fixBrokenCar(new Car[Red, T]{})
    res3: Car[Red,F] = $anon$1@67d9a642
    

    要“摧毁”它:

    def destroyRedCar(c: Car[Red, _]) = true
    
    scala> destroyRedCar(fixBrokenCar(new Car[Red, T]{}))
    res10: Boolean = true
    
    scala> destroyRedCar(new Car[Red, T]{})
    res11: Boolean = true
    
    scala> destroyRedCar(new Car[Blue, T]{})
    <console>:15: error: type mismatch;
     found   : Car[Blue,T]
     required: Car[Red, ?]
                  destroyRedCar(new Car[Blue, T]{})
                                ^
    

    如果您需要“变异”Cr 类型(更准确地说,从另一种类型构造一个类型):

    trait Car[Clr <: Color, Brkn <: Bool] { 
       type Copy[C <: Color, B <: Bool] <: Car[C,B] // define "copy" type-method
    }
    
    trait BrandedCar[Clr <: Color, Brkn <: Bool] extends Car[Clr, Brkn] {
       type Copy[C <: Color, B <: Bool] = BrandedCar[C, B] // implement "copy" type-method
       def brand: String = "default"
    }
    
    def fixBrokenCar[Cr <: Car[Red, T]](c: Cr) = c.asInstanceOf[Cr#Copy[Red, F]]
    
    def checkBrandedCar(c: BrandedCar[_, F]) = true // accepts only branded and fixed
    
    
    scala> checkBrandedCar(new BrandedCar[Red, F]{})
    res10: Boolean = true
    
    scala> checkBrandedCar(new Car[Red, F]{})
    <console>:15: error: type mismatch;
     found   : Car[Red,F]
     required: BrandedCar[?, F]
                  checkBrandedCar(new Car[Red, F]{})
                                  ^
    
    scala> checkBrandedCar(fixBrokenCar(new BrandedCar[Red, T]{}))
    res12: Boolean = true
    

    您还可以在 Car 的 trait 中定义一些 def copy[C &lt;: Color, B &lt;: Bool]: Copy[C, B] 方法(例如在案例类中),而不仅仅是 asInstanceOf

    【讨论】:

    • 如果我创建fixBrokenCar(new Car[Red, T] { def brand = "honda" }),我将失去brand。有什么办法吗?
    • 简单,只需将trait Car[Clr &lt;: Color, Brkn &lt;: Bool]{def brand: String} 添加到您的特征定义中
    • 顺便说一句,你不会在 fixBrokenCar 中丢失它,因为 scala 会为你生成结构类型:Car[Red,F]{def brand: String} - 你会丢失它 destroyRedCar,因为它忽略了 brand 属性 - 无论哪种方式,特征内的明确定义将有所帮助
    • 假设我创建了trait BrandedCar[Clr &lt;: Color, Brkn &lt;: Bool] extends Car[Clr, Brkn] { def brand: String }。我可以用fixBrokenCar 做任何事情,这样如果我传递BrandedCar,它仍然返回BrandedCar?还是我想错了?
    【解决方案2】:

    我假设你可以改变你的班级结构。

    实现您想要的一种方法是使用类型级编程。这里有一个很好的堆栈溢出帖子:Scala type programming resources

    这里是一些基于您的原始代码的示例代码,演示了如何使用 Scala 中的类型系统来实现这一点。

    /* 
     * Color traits I've left the same except converted the objects 
     * to classes. This would work just as well with traits. It might 
     * even be better if the Colors such as Red and Blue are traits
     * themselves that extend Color, I'm still just learning this 
     * method myself.
     */
    sealed trait Color
    class Red extends Color
    class Blue extends Color
    
    /* New trait to represent whether something is broken or not */
    sealed trait IsBroken    
    class Broken extends IsBroken
    class NotBroken extends IsBroken
    
    /* Change Car trait to have two type parameters, broken and color */   
    trait Car[T <: Color, S <: IsBroken]
    
    /* fixBrokenCar signature includes whether a car is broken and it's color */
    def fixBrokenRedCar(c: Car[Red, Broken]): Car[Red, NotBroken]
        = new Car[Red, NotBroken]{}
    
    val brokenRedCar = new Car[Red, Broken]{}
    val fixedRedCar = new Car[Red, NotBroken]{}
    val brokenBlueCar = new Car[Blue, Broken]{}
    
    /* Compiles */
    fixBrokenRedCar(brokenRedCar)
    
    /* Doesn't compile */
    fixBrokenRedCar(fixedRedCar)
    
    /* Doesn't compile */
    fixBrokenRedCar(brokenBlueCar)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-08-27
      • 1970-01-01
      • 1970-01-01
      • 2013-03-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多