Pattern Match on Case Objects with Type Members

后端 未结 2 2171
南方客
南方客 2021-02-08 12:18

Scala has a nice feature to infer type parameter inside the pattern match. It also checks pattern match exhaustiveness. For example:

sealed trait PField[T]

cas         


        
2条回答
  •  自闭症患者
    2021-02-08 12:58

    All your cases that extend Field are singleton objects, so that for each subtype F of Field it holds:

     if
        a: F, b: F, b.T = X
     then
        a.T = X
    

    This does not hold in general, for example for

    class Bar { type Q }
    case class Field3(b: Bar) extends Field { type T = b.Q }
    

    it does not hold that a.T = b.T for any two values a, b of type Field3.

    So, you have to somehow guarantee that all subclasses of Field are well-behaved like Field1 and Field2, and that they are not like hypothetical Field3. You can do this by adding an implicit argument to getValue that acts as a proof that the field is well behaved. For example, proving that the field is indeed a singleton object would be sufficient.

    Here is a rough sketch:

    sealed trait Field { type T }
    case object Field1 extends Field { type T = String }
    case object Field2 extends Field { type T = Int }
    
    sealed trait UniqGuarantee[UniqueTypeAsPathDependent]
    case class S1UG[X](f: String =:= X) extends UniqGuarantee[X]
    case class S2UG[X](f: Int =:= X) extends UniqGuarantee[X]
    
    sealed trait IsSingleton[F <: Field] {
      def apply(singleton: F): UniqGuarantee[singleton.T]
    }
    
    implicit object F1_is_Singleton extends IsSingleton[Field1.type] {
      def apply(singleton: Field1.type): UniqGuarantee[singleton.T] = 
        S1UG(implicitly)
    }
    
    implicit object F2_is_Singleton extends IsSingleton[Field2.type] {
      def apply(singleton: Field2.type): UniqGuarantee[singleton.T] =
        S2UG(implicitly)
    }
    
    def getValue[F <: Field]
      (f: F)
      (implicit sing: IsSingleton[F])
    : f.T = sing(f) match {
      case S1UG(uniqGuarantee) => uniqGuarantee("abc")
      case S2UG(uniqGuarantee) => uniqGuarantee(123)
    }
    

    This implementation does typecheck, and it also shows warnings if the pattern matching is non-exhaustive.

    Admittedly, the solution is pretty heavyweight, because it requires that you implement an entire separate hierarchy of case classes and implicit objects that act as "proofs" that your Fields are indeed singletons.

    I think the solution could be shortened quite a bit, I just don't see how right now.

提交回复
热议问题