Scala中的通用值类

时间:2015-02-20 11:43:07

标签: scala generic-programming

假设我有一个Pos类型(位置)。为了获得类型安全性,列/行不表示为Int,而是表示Col(列)和Row

case class Pos(col: Col, row: Row) {
  def +(other: Pos): Pos = Pos(col + other.col, row + other.row)
}

可以添加两个位置,分别包括对行和行的求和。

类型ColRow的定义如下所示:

object Row {
  def apply(value: Int) = new Row(value)
  val zero = new Row(0)
}

object Col {
  def apply(value: Int) = new Col(value)
  val zero = new Col(0)
}

class Row(val value: Int) extends AnyVal {
  def +(other: Row): Row = Row(this.value + other.value)
}

class Col(val value: Int) extends AnyVal {
  def +(other: Col): Col = Col(this.value + other.value)
}

这一切都很好,但我有重复自己的感觉。定义几乎相同。

我可以做些什么来概括它们吗?

3 个答案:

答案 0 :(得分:2)

如果你引入Scalaz并为MonoidRow创建Col个实例,你可能不会减少你的样板,但它会缩短你的零定义并附加一些:

case class Col(i: Int) extends AnyVal
case class Row(i: Int) extends AnyVal

implicit object rowMonoid extends Monoid[Row] {
  def zero = Row(0)
  def append(a: Row, b: => Row) = Row(a.i |+| b.i)
}

implicit object colMonoid extends Monoid[Col] {
  def zero = Col(0)
  def append(a: Col, b: => Col) = Col(a.i |+| b.i)
}  

并且Monoids是可组合的,因此如果您将RowsCols存储在地图或元组等中,则可以只编写它们,而不会触及单个元素:< / p>

val pt1 = (Row(4), Col(15))
val pt2 = (Row(14), Col(5))

val res = pt1 |+| pt2
println(res) // (Row(18),Col(20))

我认为,如果使用并经常添加RowCol,我认为简化用法可以为您节省更多代码而不是担心缩减定义。

答案 1 :(得分:1)

您可以为Both Row和Col类定义一个共同特征:

trait Element {
  val value : Int
  def init(value: Int): Element
  def +(other: Element) = init(value + other.value)
}

然后使用案例类,以便您利用伴随对象的apply方法:

case class Row(value: Int) extends Element {
  def init(v: Int) = Row(v)
}

case class Col(value: Int) extends Element {
  def init(v: Int) = Col(v)
}

所以现在你可以像这样添加它们:

case class Pos(col: Element, row: Element) {
  def +(other: Pos): Pos = Pos(col + other.col, row + other.row)
}

val p1 = Pos(Col(1), Row(2))
val p2 = Pos(Col(1), Row(2))   
p1 + p2 //res2: Pos = Pos(Col(2),Row(4))

但是,这允许创建仅包含行的位置

val p3 = Pos(Row(2), Row(3))    
p1 + p3 //res3: Pos = Pos(Col(3),Row(5))

因此,第二步是绑定您的Element类型的+方法。

trait Element[T <: Element[_]] {
  val value : Int
  def init(value: Int): Element[T]
  def +(other: Element[T]) = init(value + other.value)
}
case class Row(value: Int) extends Element[Row] {
  def init(v: Int) = Row(v)
}
case class Col(value: Int) extends Element[Col] {
  def init(v: Int) = Col(v)
}
case class Pos(col: Element[Col], row: Element[Row]) {
  def +(other: Pos): Pos = Pos(col + other.col, row + other.row)
}

您得到的是,现在一行只应添加行类型的元素,而Col应该只添加Col类型的元素。您仍然可以添加两个职位:

val p1 = Pos(Col(1), Row(2))
val p2 = Pos(Col(1), Row(2))
p1 + p2 //res0: Pos = Pos(Col(2),Row(4))

但这不会编译:

val p3 = Pos(Row(2), Row(3))

答案 2 :(得分:0)

您可以在特质中使用类型变量

像这样的东西

trait TableElement{
  type T
  def +(t:T):T  
}
相关问题