Я считаю классы типов способностью добавлять метаданные, безопасные для типов, в класс.
Итак, вы сначала определяете класс для моделирования проблемной области, а затем думаете о метаданных, которые нужно добавить в нее.Такие вещи, как Equals, Hashable, Viewable и т. Д. Это создает разделение проблемной области и механизмов для использования класса и открывает подклассы, потому что класс меньше.
Кроме этого, вы можете добавлять классы типов в любом месте области видимости, а не только там, где класс определен, и вы можете изменять реализации.Например, если я вычисляю хеш-код для класса Point с помощью Point # hashCode, я ограничиваюсь этой конкретной реализацией, которая может не создать хорошего распределения значений для определенного набора точек, которые у меня есть.Но если я использую 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. Чтобы использовать отражение, чтобы определить, является ли подколлекция списком / набором.Это сложно, так как карта пуста для начала (конечно, scala помогает и с манифестами) 3. иметь класс типа бедняков, требуя от пользователя предоставить сумматор.Так что-то вроде addToMap(map, key, value, adder)
, что просто уродливо