Compile time check on some property

后端 未结 2 1969
借酒劲吻你
借酒劲吻你 2021-01-06 19:46

Given the following scala code:

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

sealed trait Car {
  def isBroken: Boolean
          


        
相关标签:
2条回答
  • 2021-01-06 20:03

    I am assuming that you can change your class structure.

    One way to achieve what you want is using Type Level Programming. There is a pretty good stack overflow post on this here: Scala type programming resources

    Here is some sample code based on your original code which demonstrates how this can be achieved using the type system within 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)
    
    0 讨论(0)
  • 2021-01-06 20:07

    You should move your data to the type-level then:

    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
    

    To "destroy" it:

    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]{})
                                ^
    

    If you need to "mutate" Cr type (construct one type from another, more precisely):

    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
    

    You may also define some def copy[C <: Color, B <: Bool]: Copy[C, B] method inside Car's trait (like in case classes) instead of just asInstanceOf.

    0 讨论(0)
提交回复
热议问题