类型安全库

时间:2016-06-16 16:09:17

标签: scala traits scala-macros

(Scala 2.11)

您好,

概述

我正在尝试创建一个具有可扩展类型系统的库。因为我的库太大而且复杂,所以我创建了以下示例,该示例完全不同但具有相似的继承和依赖结构。

我创造了一个'食人族'图书馆。它的目的是构建属于不同部落(类型)的食人族,具有以下特性:

  • 作品所有食人族都有共同的行动。
  • 作品每个部落都有自己独特的可能行动,可以在有/无其他食人的情况下发生。
  • 作品所有食人族必须属于某个部落。食人族可以是“正常”和“正常”。或者'饥饿'饥饿的食人族有比其他正常食人族更多的/其他行动。
  • 作品所有食人族都可以生孩子。一个孩子将维持生产它的食人族的部落(类型)。正常的食人者会产生正常的孩子。饥肠辘辘的食人族会产生一个饥饿的孩子。
  • 不起作用食人族可以转换为另一个部落(但必须保持其饥饿/正常状态)。
  • 不起作用采用部落食人族可以采用其他部落饥饿的食人族。采用的食人族保持其部落类型。如果饥饿的食人者采用饥饿的食人族,那么采用的食人族仍然感到饥饿。如果被正常食人者采用,则采用的将成为正常的食人族。

该库有三个特征( AnyCannibalLike AnyNormalCannibalLike AnyHungryCannibalLike ),所有其他特征和类都将从中继承。该库还具有用于创建不同部落的具体案例类的抽象类。 Please look at the following image to understand the inheritance structure

图书馆代码+ JungleCannibals和UrbanCannibals示例

/////////////////////////////////////////////////////////////////////////////////////
// Any cannibal traits and abstract classes
/////////////////////////////////////////////////////////////////////////////////////
trait AnyCannibalLike {
  type TBasic <: AnyCannibalLike
  type TNormal <: AnyCannibalLike
  type THungry <: AnyCannibalLike
  type TChild <: AnyCannibalLike

  //name of the cannibal
  protected val _name : String
  def getName() = _name

  //Any cannibal can play with any cannibal
  def playWith(anyCannibal : AnyCannibalLike) : Unit

  //Any cannibal can bring a child of the same tribe into the world.
  //If the cannibal is normal then the child will be normal
  //If the cannibal is hungry then the child will be hungry
  def birthChild(childName : String) : TChild
  protected final def completeNameOfChild(childName : String) = childName + " (child of " + getName() + ")"
}

trait AnyNormalCannibalLike[Basic <: AnyCannibalLike] extends AnyCannibalLike {
  type TBasic = Basic
  type TChild = TNormal
  //How to do this?
  //def changeTo[Basic <: AnyCannibalLike, Normal <: AnyNormalCannibalLike[Basic], Hungry <: AnyHungryCannibalLike[Basic]]() : Normal
}

trait AnyHungryCannibalLike[Basic <: AnyCannibalLike] extends AnyCannibalLike {
  type TBasic = Basic
  type TChild = THungry
  //Any hungry cannibal can eat other cannibals of the same tribe
  def eat(sameCannibal : Basic) : Unit
  //How to do this?
  //def changeTo[Basic <: AnyCannibalLike, Normal <: AnyNormalCannibalLike[Basic], Hungry <: AnyHungryCannibalLike[Basic]]() : Hungry
}

abstract class AnyNormalCannibalA[Basic <: AnyCannibalLike, Normal <: AnyNormalCannibalLike[Basic], Hungry <: AnyHungryCannibalLike[Basic]](name : String) extends AnyNormalCannibalLike[Basic] {
  type TNormal = Normal
  type THungry = Hungry
  protected val _name : String = name
  def playWith(anyCannibal : AnyCannibalLike) : Unit = {
    println(getName() + " playing with " + anyCannibal.getName())
  }
}

abstract class AnyHungryCannibalA[Basic <: AnyCannibalLike, Normal <: AnyNormalCannibalLike[Basic], Hungry <: AnyHungryCannibalLike[Basic]](name : String, eatingTool : String) extends AnyHungryCannibalLike[Basic] {
  type TNormal = Normal
  type THungry = Hungry
  protected val _name : String = name
  def playWith(anyCannibal : AnyCannibalLike) : Unit = {
    println(getName() + " playing hungrily with " + anyCannibal.getName())
  }
  def eat(sameCannibal : Basic) : Unit = {
    println(getName() + " eating " + sameCannibal.asInstanceOf[AnyCannibalLike].getName() + " using " + eatingTool)
  }
}
/////////////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////////////
// Jungle cannibal traits and case classes
/////////////////////////////////////////////////////////////////////////////////////
trait JungleCannibalLike extends AnyCannibalLike {
  //Jungle cannibals can hunt with other jungle cannibals
  def huntWith(jungleCannibal : JungleCannibalLike) : Unit = {
    println(getName() + " hunting with " + jungleCannibal.getName())
  }
}

trait JungleNormalCannibalLike extends AnyNormalCannibalLike[JungleCannibalLike] with JungleCannibalLike

trait JungleHungryCannibalLike extends AnyHungryCannibalLike[JungleCannibalLike] with JungleCannibalLike {
  //Jungle cannibals can hunt with and then eat other jungle cannibals
  def huntAndEat(jungleCannibal: JungleCannibalLike) : Unit = {
    huntWith(jungleCannibal)
    eat(jungleCannibal)
  }
}

case class JungleNormalCannibal(name : String) extends AnyNormalCannibalA[JungleCannibalLike, JungleNormalCannibalLike, JungleHungryCannibalLike](name) with JungleNormalCannibalLike {
  def birthChild(childName : String) : TChild = JungleNormalCannibal(completeNameOfChild(childName))
}
case class JungleHungryCannibal(name : String) extends AnyHungryCannibalA[JungleCannibalLike, JungleNormalCannibalLike, JungleHungryCannibalLike](name,"hands") with JungleHungryCannibalLike {
  def birthChild(childName : String) : TChild = JungleHungryCannibal(completeNameOfChild(childName))
}
/////////////////////////////////////////////////////////////////////////////////////


/////////////////////////////////////////////////////////////////////////////////////
// Urban cannibal traits and case classes
/////////////////////////////////////////////////////////////////////////////////////
trait UrbanCannibalLike extends AnyCannibalLike {
  //Urban cannibals can read with other urban cannibals
  def readWith(urbanCannibal : UrbanCannibalLike) : Unit = {
    println(getName() + " reading with " + urbanCannibal.getName())
  }
}

trait UrbanNormalCannibalLike extends AnyNormalCannibalLike[UrbanCannibalLike] with UrbanCannibalLike

trait UrbanHungryCannibalLike extends AnyHungryCannibalLike[UrbanCannibalLike] with UrbanCannibalLike {
  //Urban cannibals can read with and then eat other urban cannibals
  def readAndEat(urbanCannibal: UrbanCannibalLike) : Unit = {
    readWith(urbanCannibal)
    eat(urbanCannibal)
  }
}

case class UrbanNormalCannibal(name : String) extends AnyNormalCannibalA[UrbanCannibalLike, UrbanNormalCannibalLike, UrbanHungryCannibalLike](name) with UrbanNormalCannibalLike {
  def birthChild(childName : String) : TChild = UrbanNormalCannibal(completeNameOfChild(childName))
}
case class UrbanHungryCannibal(name : String) extends AnyHungryCannibalA[UrbanCannibalLike, UrbanNormalCannibalLike, UrbanHungryCannibalLike](name,"fork") with UrbanHungryCannibalLike {
  def birthChild(childName : String) : TChild = UrbanHungryCannibal(completeNameOfChild(childName))
}
/////////////////////////////////////////////////////////////////////////////////////



/////////////////////////////////////////////////////////////////////////////////////
// Adopting cannibal traits and case classes
/////////////////////////////////////////////////////////////////////////////////////
trait AdoptingCannibalLike extends AnyCannibalLike {
  //Adopting cannibals can adopt any hungry cannibal.
  //If the adopter is hungry then the returned adopted should keep its type as is.
  //If the adopter is not hungry then the returned adopted is the same type but not hungry.
  //how to do this?
  //def adoptHungryCannibal(hungryCannibal : AnyHungryCannibalLike[_]) : hungryCannibal.type = hungryCannibal
  //val ajhc = adoptHungryCannibal(JungleHungryCannibal("jhc"))
}

trait AdoptingNormalCannibalLike extends AnyNormalCannibalLike[AdoptingCannibalLike] with AdoptingCannibalLike
trait AdoptingHungryCannibalLike extends AnyHungryCannibalLike[AdoptingCannibalLike] with AdoptingCannibalLike

case class AdoptingNormalCannibal(name : String) extends AnyNormalCannibalA[AdoptingCannibalLike, AdoptingNormalCannibalLike, AdoptingHungryCannibalLike](name) with AdoptingNormalCannibalLike {
  def birthChild(childName : String) : TChild = AdoptingNormalCannibal(completeNameOfChild(childName))
}
case class AdoptingHungryCannibal(name : String) extends AnyHungryCannibalA[AdoptingCannibalLike, AdoptingNormalCannibalLike, AdoptingHungryCannibalLike](name,"my kids") with AdoptingHungryCannibalLike {
  def birthChild(childName : String) : TChild = AdoptingHungryCannibal(completeNameOfChild(childName))
}
/////////////////////////////////////////////////////////////////////////////////////

测试代码

val jnc = JungleNormalCannibal("jnc")
val jhc = JungleHungryCannibal("jhc")
val unc = UrbanNormalCannibal("unc")
val uhc = UrbanHungryCannibal("uhc")

val cjnc = jnc.birthChild("cjnc")
val cjhc = jhc.birthChild("cjhc")

//Must execute
jnc.playWith(jnc)
jnc.playWith(unc)
jhc.eat(jnc)
uhc.eat(unc)
jnc.huntWith(jhc)
jhc.huntWith(jnc)
cjnc.huntWith(jhc)
cjhc.huntAndEat(jnc)

//Must produce compilation errors
jnc.huntWith(unc)
jnc.eat(jnc)
cjnc.huntWith(unc)
cjnc.eat(jnc)

问题

  1. 无法实现changeTo [,,]创建一个同名但不同类型的新食人族(我猜测需要使用implicits和宏来实现这一目标,但我不知道如何。
  2. 无法实施采用HungryCannibal。类似的问题#1?
  3. 代码似乎太冗长了。为了创建一个新的部落,我们重复了很多代码。有关如何简化它的任何建议(宏,依赖注入)?
  4. 备注

    由于我是Scala的新手,我可能没有编写传统代码。如果你看到任何错误,请指出错误。

0 个答案:

没有答案