На прошлой неделе мой друг представлял собой, казалось бы, безобидный вопрос языка Scala, на который у меня не было хорошего ответа: есть ли простой способ объявить коллекцию вещей, принадлежащих к некоторому обычному классу , Конечно, в Scala нет первоклассного понятия «typeclass», поэтому мы должны думать об этом с точки зрения черт и границ контекста (т. Е. Implicits).Импрессивные типы против простого старого подтипирования
В частности, учитывая некоторые черты T[_]
представляющих класс типов, а также типов A
, B
и C
с соответствующим implicits в рамках T[A]
, T[B]
и T[C]
, мы хотим объявить что-то вроде List[T[a] forAll { type a }]
, в которую мы можем бросить экземпляры A
, B
и C
с безнаказанностью. Это, конечно, не существует в Scala; a question last year обсуждает это более подробно.
Естественный вопрос о последующей деятельности - «как это делает Хаскелл?» Ну, GHC, в частности, имеет расширение системы типа impredicative polymorphism, описанное в документе "Boxy Types". Короче говоря, с учетом спецификации T
можно юридически построить список [forall a. T a => a]
. Учитывая объявление этой формы, компилятор выполняет некоторую магию передачи словаря, которая позволяет нам сохранять экземпляры typeclass, соответствующие типам каждого значения в списке во время выполнения.
Вещь, «передача словарям» звучит так же, как «vtables». В объектно-ориентированном языке, таком как Scala, подтипирование является гораздо более простым, естественным механизмом, чем подход «Типы Boxy». Если наши A
, B
и C
все расширят признак T
, тогда мы можем просто объявить List[T]
и быть счастливыми. Аналогично, как отмечает Майлз в комментарии ниже, если все они расширят черты , T2
и T3
, то я могу использовать List[T1 with T2 with T3]
в качестве эквивалента импрессивного Haskell [forall a. (T1 a, T2 a, T3 a) => a]
.
Однако основной, хорошо известный недостаток с подтипами по сравнению с классами типов является тесной связью: мои A
, B
и C
типов должны иметь их T
поведения, запеченные в Давайте предположим, что это крупный dealbreaker, и я могу. 't использование подтипирование. Таким образом, середина в Scala является сутенеры^преобразования H^H^H^H^Himplicit: даны некоторые A => T
, B => T
и C => T
в неявной области, я могу снова вполне счастливо заселить List[T]
с моим A
, B
и C
ценностей ...
... до List[T1 with T2 with T3]
. В этот момент, даже если у нас есть неявные преобразования A => T1
, A => T2
и A => T3
, мы не можем поместить в список A
. Мы могли бы реструктурировать наши неявные преобразования, чтобы буквально предоставить A => T1 with T2 with T3
, но я никогда не видел, чтобы кто-то делал это раньше, и это похоже на еще одну форму жесткой связи.
Итак, мой вопрос, наконец, я полагаю, сочетание пару вопросов, которые ранее попросили здесь: "why avoid subtyping?" и "advantages of subtyping over typeclasses" ... есть некоторая объединяющая теория, которая говорит непредикативный полиморфизм и подтип полиморфизм один и те же ? Являются ли скрытые преобразования каким-то тайным любовным ребенком этих двух? И может ли кто-нибудь сформулировать хороший, чистый шаблон для выражения нескольких границ (как в последнем примере выше) в Scala?
Возможно, это будет актуально: http://stackoverflow.com/questions/7213676/forall-in-scala – missingfaktor
@missingfaktor это, безусловно, есть, вот почему я связал его! – mergeconflict
Аа, простите, пропустил это в первом чтении! – missingfaktor