简体   繁体   English

F界多态性中子类型的Scala重写类型参数

[英]Scala rewriting type parameter of sub type in F-bounded polymorphism

I am trying to create a trait Entity which enforces its sub types to have 2 states: Transient and Persistent 我正在尝试创建一个特征Entity ,该Entity将其子类型强制具有两种状态: TransientPersistent

trait EntityState
trait Transient extends EntityState
trait Persistent extends EntityState
trait Entity[State <: EntityState]

For example, a sub class, says class Post[State <: EntityState] extends Entity[State] , can be instantiated either as new Post[Persistent] or as new Post[Transient] . 例如, class Post[State <: EntityState] extends Entity[State]的子类可以实例化为new Post[Persistent]new Post[Transient]

Next, I am adding some methods to the trait Entity that can be called depending on its State : 接下来,我向特征Entity添加一些方法,可以根据其State调用它们:

trait Entity[State <: EntityState] {
    def id(implicit ev: State <:< Persistent): Long
    def persist(implicit ev: State <:< Transient): Entity[Persistent]
}

To explain, for any class that extends Entity , the method id can be called only when the class is of state Persistent (ie it has been saved to the database and already assigned an autogenerated id). 解释一下,对于任何扩展Entity类,仅当该类的状态为Persistent (即已保存到数据库并已分配了自动生成的ID)时,才可以调用方法id

On the other hand, the method persist can be called only when the class is Transient (not yet saved to the database). 另一方面,仅当类为Transient (尚未保存到数据库)时才可以调用persist方法。 The method persist is meant to save an instance of the caller class to the database and return the Persistent version of the class. 方法persist是用于将调用者类的实例保存到数据库中并返回该类的Persistent版本。

Now, the problem is I would like that the return type of persist be that of the caller class . 现在,问题是我希望persist的返回类型是调用方类的返回类型 For example, if I call persist on an instance of class Post[Transient] , it should return Post[Persistent] instead of Entity[Persistent] . 例如,如果我在类Post[Transient]的实例上调用persist ,则它应返回Post[Persistent]而不是Entity[Persistent]

I searched around and found something called F-Bounded Polymorphism . 我四处搜寻,发现了一个叫做F界多态的东西。 I am trying many ways to adapt it to solve my problem but still not works. 我正在尝试多种方法来适应它来解决我的问题,但仍然行不通。 Here is what I did: 这是我所做的:

First try: 第一次尝试:

trait Entity[State <: EntityState, Self[_] <: Entity[State,Self]] {
    def id(implicit ev: State <:< Persistent): Long
    def persist(implicit ev: State <:< Transient): Self[Persistent]
}

and

class Post[State <: EntityState] extends Entity[State, ({type λ[B] == Post[State]})#λ] {

    def persist(implicit ev: <:<[State, Transient]): Post[State] = {
        ???
    }
}

In the class Post above, I use auto-completion of Eclipse to generate the implementation of the method persist and found that its return type is still incorrect. 在上面的Post类中,我使用Eclipse的自动完成功能来生成persist方法的实现,并发现其返回类型仍然不正确。

Second try: 第二次尝试:

class Post[State <: EntityState] extends Entity[State, Post] {

   def persist(implicit ev: <:<[State, Transient]): Post[Persistent] = {
       ???
   }
}

with this, it seems correct, except it has a compilation error: 这样做似乎是正确的,除了它有一个编译错误:

[error] D:\playspace\myblog\app\models\post\Post.scala:14: kinds of the type arguments (State,models.post.Post) do not conform to the expected kinds of the type parameters (type State,type Self) in trait Entity.
[error] models.post.Post's type parameters do not match type Self's expected parameters:
[error] type State's bounds <: common.models.EntityState are stricter than type _'s declared bounds >: Nothing <: Any
[error] trait Post[State <: EntityState] extends Entity[State, Post] {

I believe this is what you were trying to do: 我相信这是您想要做的:

trait Entity[State <: EntityState, Self[S<:EntityState] <: Entity[S, Self]] {
  _: Self[State] =>

    def id(implicit ev: State <:< Persistent): Long
    def persist(implicit ev: State <:< Transient): Self[Persistent]
}


abstract class Post[State <: EntityState] extends Entity[State, Post] {
   def persist(implicit ev: <:<[State, Transient]): Post[Persistent] = {
       ???
   }
}

UPDATE: The _: Self[State] => part is a self-type annotation . 更新: _: Self[State] =>部分是一个自类型注释 It says that any class that mixes the Entity trait must extend Self[State] (not doing so would lead to a compile-time error). 它说任何混合了Entity特性的类都必须扩展Self[State] (不这样做会导致编译时错误)。 If we remove this self-type annotation, we might define something like this and the compiler would not blink an eye: 如果删除此自类型注释,则可能会定义如下内容,并且编译器不会眨眼:

abstract class User[State <: EntityState] extends Entity[State, Post] {
   def persist(implicit ev: <:<[State, Transient]): Post[Persistent] = {
       ???
   }
}

Note how out User class extends Entity with the Self type parameter set to Post (instead of User ). 请注意User类如何通过将Self类型参数设置为Post (而不是User )扩展Entity This is valid as far as the compiler is concerned, but was certainly not what you had in mind. 就编译器而言,这是有效的,但肯定不是您所想到的。 With the self-type annotation, the above would not compile: 使用自类型注释时,以上内容将无法编译:

<console>:12: error: illegal inheritance;
 self-type User[State] does not conform to Entity[State,Post]'s selftype Post[State]
           abstract class User[State <: EntityState] extends Entity[State, Post] {
                                                             ^

Why doesn't plain old ad-hoc polymorphism satisfy you here? 为什么普通的旧式临时多态性在这里不满意? Eg, this compiles. 例如,此编译。

trait EntityState
trait Transient extends EntityState
trait Persistent extends EntityState
trait Entity[State <: EntityState] {
  def id(implicit ev: State <:< Persistent): Long
  def persist(implicit ev: State <:< Transient): Entity[Persistent]
}

class Post[State <: EntityState] extends Entity[State] {
  override def id(implicit ev: <:<[State, Persistent]): Long = 5
  override def persist(implicit ev: <:<[State, Transient]): Post[Persistent] = new Post[Persistent]
}

// compiles
val x: Post[Persistent] = (new Post[Transient]).persist
// doesn't compile, as desired
// val y = (new Post[Persistent]).persist

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM