В Scala мы можем использовать как минимум два метода для модификации существующих или новых типов. Предположим, мы хотим выразить, что что-то можно количественно оценить с помощью Int
. Мы можем определить следующую черту.
Неявное преобразование
trait Quantifiable{ def quantify: Int }
И затем мы можем использовать неявные преобразования для количественной оценки, например, строк и списков.
implicit def string2quant(s: String) = new Quantifiable{
def quantify = s.size
}
implicit def list2quantifiable[A](l: List[A]) = new Quantifiable{
val quantify = l.size
}
После их импорта мы можем вызывать метод quantify
для строк и списков. Обратите внимание, что количественный список сохраняет свою длину, поэтому он позволяет избежать дорогостоящего обхода списка при последующих вызовах quantify
.
Типовые классы
Альтернативой является определение «свидетеля», Quantified[A]
который утверждает, что некоторый тип A
может быть определен количественно.
trait Quantified[A] { def quantify(a: A): Int }
Затем мы предоставляем экземпляры этого типа класса для String
и List
где-нибудь.
implicit val stringQuantifiable = new Quantified[String] {
def quantify(s: String) = s.size
}
И если мы затем напишем метод, который должен количественно оценить свои аргументы, мы напишем:
def sumQuantities[A](as: List[A])(implicit ev: Quantified[A]) =
as.map(ev.quantify).sum
Или используя синтаксис, связанный с контекстом:
def sumQuantities[A: Quantified](as: List[A]) =
as.map(implicitly[Quantified[A]].quantify).sum
Но когда какой метод использовать?
Теперь возникает вопрос. Как я могу выбрать между этими двумя концепциями?
То, что я заметил до сих пор.
типовые классы
- классы типов допускают красивый синтаксис с привязкой к контексту
- с классами типов я не создаю новый объект-оболочку при каждом использовании
- синтаксис, связанный с контекстом, больше не работает, если класс типа имеет несколько параметров типа; представьте, что я хочу количественно оценить вещи не только с помощью целых чисел, но и со значениями некоторого общего типа
T
. Я бы хотел создать класс типаQuantified[A,T]
неявное преобразование
- поскольку я создаю новый объект, я могу кэшировать там значения или вычислить лучшее представление; но следует ли мне этого избегать, поскольку это может происходить несколько раз, а явное преобразование, вероятно, будет вызвано только один раз?
Чего я жду от ответа
Представьте один (или несколько) вариантов использования, в которых разница между обеими концепциями имеет значение, и объясните, почему я предпочитаю одно другому. Также было бы неплохо объяснить суть двух концепций и их связь друг с другом, даже без примеров.