代码之家  ›  专栏  ›  技术社区  ›  OlivierBlanvillain

尝试使用类型参数编写DRY Scala代码

  •  3
  • OlivierBlanvillain  · 技术社区  · 10 年前

    我已经定义了一些case类,它们都有一个字段id:id[T],其中T是case类的类型。我希望有一个特性来强制这个属性,以便在这些类上编写泛型代码。

    我的第一次尝试是在trait上使用类型参数和自类型来强制执行此属性:

    case class Id[M](value: Long)
    
    trait EnforceIdType[T] {
      this: T =>
      val id: Id[T]
    }
    
    case class A(id: Id[A]) extends EnforceIdType[A]
    case class B(id: Id[B]) extends EnforceIdType[B]
    // case class C(id: Id[B]) extends EnforceIdType[B] < Won't compile, as expected
    

    这很好,但我想知道是否有一种方法可以做到这一点,而不需要对特征使用类型参数。这是我的第二次尝试:

    case class Id[M](value: Long)
    
    trait EnforceIdType {
      val id: Id[_ <: EnforceIdType]
    }
    
    case class A(id: Id[A]) extends EnforceIdType
    case class B(id: Id[B]) extends EnforceIdType
    case class C(id: Id[B]) extends EnforceIdType // Compiles :(
    

    具体来说,我希望使用case类的这些定义(不将类型参数传递给EnforceIdType),但仍然强制执行属性(这个C不应该编译)。有什么办法吗?我感觉像 this.type 可以在这里使用,但我不是很熟悉。

    1 回复  |  直到 10 年前
        1
  •  6
  •   Yury    10 年前

    你是对的, this.type 需要使用,但有一些限制:

    trait EnforceIdType {
      def id: Id[_ >: this.type <: EnforceIdType]
    }
    
    case class A(id: Id[A]) extends EnforceIdType
    case class B(id: Id[B]) extends EnforceIdType
    //case class C(id: Id[B]) extends EnforceIdType // Won't compile, as expected
    

    更新时间:

    关于第二个限制(由Alexey Romanov展示)。它可以消除,但需要很长的路:

    //Embeded polymorphism used
    class Id(val value: Long) {
      type M
    }
    
    // Factory method for shift type variable to type parameter field
    object Id {
      def apply[T](value : Long) = new Id(value) { type M = T }
    }
    
    trait EnforceIdType {
      type This = this.type // store this.type for use inside Id { type M = .. }
      val id: Id { type M >: This <: EnforceIdType } // need to be val
    
      def show(x : id.M) { println (x) } // dependent method
    }
    
    case class A(id: Id { type M = A }) extends EnforceIdType
    case class B(id: Id { type M = B }) extends EnforceIdType
    //  case class C(id: Id { type M = B }) extends EnforceIdType // Won't compile, as expected
    
    val a = A( Id[A](10))
    val b = B( Id[B](10))
    
    a.show(a)
    // a.show(b) // Won't compile, as expected