我认为类型类是向类添加类型安全元数据的能力。
因此,您首先定义一个类来为问题域建模,然后考虑添加元数据。诸如 Equals、Hashable、Viewable 之类的东西。这创建了问题域和使用类的机制的分离,并打开了子类化,因为类更精简。
除此之外,您可以在范围内的任何位置添加类型类,而不仅仅是定义类的位置,并且您可以更改实现。例如,如果我使用 Point#hashCode 计算 Point 类的哈希码,那么我将仅限于特定的实现,这可能无法为我拥有的特定点集创建良好的值分布。但是如果我使用 Hashable[Point],那么我可以提供自己的实现。
[更新示例]
例如,这是我上周的一个用例。在我们的产品中,有几种地图包含容器作为值的情况。例如,Map[Int, List[String]] 或 Map[String, Set[Int]]。添加到这些集合中可能很冗长:
map += key -> (value :: map.getOrElse(key, List()))
所以我想有一个函数来包装它,这样我就可以写了
map +++= key -> value
主要问题是这些集合并不都有相同的添加元素的方法。有些有'+',而另一些有':+'。我还想保留将元素添加到列表的效率,所以我不想使用创建新集合的 fold/map。
解决方案是使用类型类:
trait Addable[C, CC] {
def add(c: C, cc: CC) : CC
def empty: CC
}
object Addable {
implicit def listAddable[A] = new Addable[A, List[A]] {
def empty = Nil
def add(c: A, cc: List[A]) = c :: cc
}
implicit def addableAddable[A, Add](implicit cbf: CanBuildFrom[Add, A, Add]) = new Addable[A, Add] {
def empty = cbf().result
def add(c: A, cc: Add) = (cbf(cc) += c).result
}
}
这里我定义了一个类型类Addable,它可以将元素C添加到集合CC中。我有 2 个默认实现:对于使用 :: 的列表和对于其他集合,使用构建器框架。
那么使用这个类型类就是:
class RichCollectionMap[A, C, B[_], M[X, Y] <: collection.Map[X, Y]](map: M[A, B[C]])(implicit adder: Addable[C, B[C]]) {
def updateSeq[That](a: A, c: C)(implicit cbf: CanBuildFrom[M[A, B[C]], (A, B[C]), That]): That = {
val pair = (a -> adder.add(c, map.getOrElse(a, adder.empty) ))
(map + pair).asInstanceOf[That]
}
def +++[That](t: (A, C))(implicit cbf: CanBuildFrom[M[A, B[C]], (A, B[C]), That]): That = updateSeq(t._1, t._2)(cbf)
}
implicit def toRichCollectionMap[A, C, B[_], M[X, Y] <: col
特殊位使用adder.add 添加元素并使用adder.empty 为新键创建新集合。
比较一下,如果没有类型类,我会有 3 个选项:
1.为每个集合类型编写一个方法。例如,addElementToSubList 和 addElementToSet 等。这会在实现中创建大量样板文件并污染命名空间
2.使用反射判断子集合是否为List/Set。这很棘手,因为地图一开始是空的(当然 scala 在这里也有助于 Manifests)
3. 要求用户提供加法器,具有穷人型等级。所以像addToMap(map, key, value, adder) 这样的东西很丑