基于函数的参数化类型强制对函数进行参数和返回类型限制

时间:2016-12-14 12:17:40

标签: scala

我通过Scala对象和类型系统学习我的方式,作为练习,我决定实施赛亚人训练应用程序,赛亚人可以训练其他赛亚人升级。

    class Saiyan
    class SuperSaiyan extends Saiyan
    class SuperSaiyan2 extends SuperSaiyan
    class SuperSaiyan3 extends SuperSaiyan2

    trait Trainer[A <: Saiyan]{
      def train(trainee: A):A
    }

    val noob = new Saiyan
    val goku = new SuperSaiyan2 with Trainer[SuperSaiyan2] {
        //needs override define of train method.
    }

    val trainedNoob = goku.train(noob)

Trait Trainer表示非常肤浅。我想对列车方法有一些限制,如下所示:

  1. SuperSaiyan2的列车方法可以接受Saiyan
    的实例 SuperSaiyanSuperSaiyan2 但不是 SuperSaiyan3
  2. SuperSaiyan2无法培训受训者成为SuperSaiyan3
  3. 无论SuperSaiyan2采取什么样的受训者都无法降级。它必须始终保持升级或保持不变。
  4. 如果可能的话,我如何在这段代码中实现这一点?

3 个答案:

答案 0 :(得分:3)

免责声明:在给定的条款中,此问题无法解决。

具体而言,您无法在逻辑上满足此要求

  1. 任何SuperSaiyan2个实例都可以在SuperSaiyan2
  2. 之上训练任何内容
  3. SuperSaiyan3实例可以培训SuperSayian3
  4. SuperSaiyan3SuperSaiyan2
  5. 的子类型

    正如您在3.看到的那样,Supersayan3实例也是SuperSaiyan2个实例,因此1.2.会产生矛盾

    但是你可以尝试设置一些边界,使它们在类型级别上抽象,但在实例级别

    指定

    UPDATE:

    层次结构半解决方案

    使用给定术语和示例编译的最简单方法是

    class Saiyan {
      type Level <: Saiyan
      def train[T <: Saiyan](trainee: T)(implicit ev: Level <:< trainee.Level): Saiyan = Saiyan()
    }
    
    object Saiyan {
      def apply() = new Saiyan {type Level = Saiyan}
    }
    
    class SuperSaiyan extends Saiyan {
      type Level <: SuperSaiyan
      override def train[T <: Saiyan](trainee: T)(implicit ev: Level <:< trainee.Level): SuperSaiyan = SuperSaiyan()
    }
    
    object SuperSaiyan {
      def apply() = new SuperSaiyan {type Level = SuperSaiyan}
    }
    
    class SuperSaiyan2 extends SuperSaiyan {
      type Level <: SuperSaiyan
      override def train[T <: Saiyan](trainee: T)(implicit ev: Level <:< trainee.Level): SuperSaiyan2 = SuperSaiyan2()
    }
    
    object SuperSaiyan2 {
      def apply() = new SuperSaiyan2 {type Level = SuperSaiyan2}
    }
    
    class SuperSaiyan3 extends SuperSaiyan2 {
      type Level <: SuperSaiyan3
      override def train[T <: Saiyan](trainee: T)(implicit ev: Level <:< trainee.Level): SuperSaiyan3 = SuperSaiyan3()
    }
    
    object SuperSaiyan3 {
      def apply() = new SuperSaiyan3 {type Level = SuperSaiyan3}
    }
      override def train[T <: Saiyan](trainee: T)(implicit ev: UpperBound <:< trainee.UpperBound): SuperSaiyan3 = SuperSaiyan3()
    }
    
    object SuperSaiyan3 {
      def apply() = new SuperSaiyan3 {type UpperBound = SuperSaiyan3}
    }
    

    此代码将成功编译代码,如

    val goku = SuperSaiyan2()
    
    goku.train(Saiyan())
    

    但进一步

    goku.train(goku.train(Saiyan()))
    

    会失败,因为在type Level之后train的信息丢失了。在我以最佳方法计算train之后使这种类型可用会导致循环类型引用,这在当前的scala中是非法的。

    类型类

    因此,如果您确实需要具有部分顺序的类型级别层次结构,则可以为该

    引入一些类型类
    class CanTrain[+A, B]
    trait SelfCanTrain[T] {
      implicit val canTrainSelf: CanTrain[T, T] = new CanTrain
    }
    
    class Saiyan {
      def train[T](trainee: T)(implicit ev: CanTrain[T, Saiyan]) = new Saiyan
    }
    
    object Saiyan extends SelfCanTrain[Saiyan]
    
    class SuperSaiyan extends Saiyan {
      def train[T](trainee: T)(implicit ev: CanTrain[T, SuperSaiyan]) = new SuperSaiyan
    }
    
    object SuperSaiyan extends SelfCanTrain[SuperSaiyan] 
    
    class SuperSaiyan2 extends SuperSaiyan {
      def train[T](trainee: T)(implicit ev: CanTrain[T, SuperSaiyan2])= new SuperSaiyan2
    }
    
    object SuperSaiyan2 extends SelfCanTrain[SuperSaiyan2]
    
    class SuperSaiyan3 extends SuperSaiyan2 {
      def train[T](trainee: T)(implicit ev: CanTrain[T, SuperSaiyan3])= new SuperSaiyan3
    }
    
    object SuperSaiyan3 extends SelfCanTrain[SuperSaiyan3]
    

    从现在起非常传递

    goku.train(goku.train(goku.train(new Saiyan)))
    

    将成功编译

答案 1 :(得分:0)

我认为这就是你正在寻找的东西

trait Trainer[A <: Saiyan]{ def train[B >: A, C >:A <:B](trainee: B) : C }

因此,train方法需要两个额外的类型参数:

  • B必须是A的超级类型。B >: A解决1。
  • C,它也必须是超级类型的A. C >: A解决3。),但也是B .. <: B的子类型(解决2。

    编辑:解决方案错误;请参阅Odomontois关于原因的答案。

答案 2 :(得分:0)

尝试使用无形Nat限制基于赛亚人等级的编译

import shapeless.Nat
import shapeless.nat._
import shapeless.ops.nat._

trait Saiyan[A <: Nat] {
   def train[B <: Nat, C <: Nat](b: Saiyan[B])(implicit blt: B LT A): Saiyan[C] =
     ???
}

class SuperSaiyan extends Saiyan[_1]
class SuperSaiyan2 extends Saiyan[_2]
class SuperSaiyan3 extends Saiyan[_3]


val noob = new Saiyan[_0] {}
val goku = new SuperSaiyan2

现在行goku.train(noob)已编译,但noob.train(goku)没有。