我通过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
表示非常肤浅。我想对列车方法有一些限制,如下所示:
SuperSaiyan2
的列车方法可以接受Saiyan
,SuperSaiyan
和SuperSaiyan2
但不是 SuperSaiyan3
。 SuperSaiyan2
无法培训受训者成为SuperSaiyan3
。SuperSaiyan2
采取什么样的受训者都无法降级。它必须始终保持升级或保持不变。如果可能的话,我如何在这段代码中实现这一点?
答案 0 :(得分:3)
具体而言,您无法在逻辑上满足此要求
SuperSaiyan2
个实例都可以在SuperSaiyan2
SuperSaiyan3
实例可以培训SuperSayian3
SuperSaiyan3
是SuperSaiyan2
正如您在3.
看到的那样,Supersayan3
实例也是SuperSaiyan2
个实例,因此1.
和2.
会产生矛盾
但是你可以尝试设置一些边界,使它们在类型级别上抽象,但在实例级别
指定使用给定术语和示例编译的最简单方法是
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
(解决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)
没有。