На прошлой неделе мой друг задал, казалось бы, безобидный вопрос о языке Scala, на который у меня не было хорошего ответа: есть ли простой способ объявить набор вещей, принадлежащих какому-то общему классу типов. Конечно, в Scala нет первоклассного понятия «класс типов», поэтому мы должны думать об этом в терминах трейтов и границ контекста (то есть имплицитов).
Конкретно, учитывая некоторую черту T[_]
, представляющую класс типов, и типы A
, B
и C
с соответствующими имплицитами в области видимости T[A]
, T[B]
и T[C]
, мы хотим объявить что-то вроде List[T[a] forAll { type a }]
, в которое мы можем бросать экземпляры A
, B
и C
безнаказанно. Этого, конечно, нет в Scala; вопрос прошлого года обсуждает это более подробно.
Естественным последующим вопросом является «как это делает Haskell?» Что ж, у GHC, в частности, есть расширение системы типов под названием непредикативный полиморфизм, описанный в "Boxy Types" paper. Короче говоря, имея класс типов T
, можно легально создать список [forall a. T a => a]
. Учитывая объявление этой формы, компилятор выполняет некоторую магию передачи словаря, которая позволяет нам сохранять экземпляры класса типов, соответствующие типам каждого значения в списке во время выполнения.
Дело в том, что «магия передачи словаря» очень похожа на «vtables». В объектно-ориентированном языке, таком как Scala, выделение подтипов - гораздо более простой и естественный механизм, чем подход «Boxy Types». Если наши A
, B
и C
расширяют черту T
, тогда мы можем просто объявить List[T]
и быть счастливыми. Точно так же, как отмечает Майлз в комментарии ниже, если все они расширяют черты T1
, T2
и T3
, тогда я могу использовать List[T1 with T2 with T3]
как эквивалент предполагаемого Haskell [forall a. (T1 a, T2 a, T3 a) => a]
.
Однако главный, хорошо известный недостаток подтипов по сравнению с классами типов - это тесная связь: мои A
, B
и C
типы должны иметь свое T
поведение. Предположим, это серьезное нарушение правил, а я не могу использовать подтипы. Таким образом, золотая середина в Scala - это сутенеры ^ H ^ H ^ H ^ H ^ H Явные преобразования: учитывая некоторые 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
, но я никогда не видел, чтобы кто-то делал это раньше, и это похоже на еще одну форму тесной связи.
Итак, мой вопрос, я полагаю, представляет собой комбинацию из пары вопросов, которые ранее задавались здесь: «зачем избегать подтипов? " и « преимущества подтипов над классами типов » ... существует ли какая-то объединяющая теория, которая утверждает, что импредикативный полиморфизм и полиморфизм подтипа - это одно и то же? Могут ли неявные преобразования каким-то образом быть тайным дитем любви этих двоих? И может ли кто-нибудь сформулировать хороший чистый шаблон для выражения нескольких границ (как в последнем примере выше) в Scala?
List[T]
является адекватным (в контексте) переводом для списка[forall a. T a => a]
, то почемуList[T1 with T2 with T3]
не является адекватным переводом для списка[forall a. (T1 a, T2 a, T3 a) => a]
? - person Miles Sabin   schedule 16.03.2012T1
,T2
иT3
, если вместо этого я могу использовать неявные преобразования. Также спасибо за возможность использовать фразу тайная любовь-дитя в вопросе о системах типов на StackOverflow :) - person mergeconflict   schedule 16.03.2012showlist :: [forall a. Show a => a]
...showlist = [()]
дает ошибкуCould not deduce (a ~ ()) from the context (Show a)
. Это должен быть худший вопрос о StackOverflow за всю историю. - person mergeconflict   schedule 17.03.2012data T where T :: Show a => a -> T
? Это даст вам желаемое поведение, за исключением того, что вам нужно обернуть каждый объект оболочкой, чтобы поместить его в список. - person Jeremiah Willcock   schedule 18.03.2012[forall a. Show a => a]
означает список всех типов, таких чтоShow a
;[()]
не удовлетворяет этому типу, поскольку()
относится к более конкретному типу()
. Я не уверен, что есть какие-то значения, которые удовлетворяют этому типу, но все равно это не то, что вам нужно; чтобы получить то, что вы хотели, вам нужна экзистенциальная квалификация, которая требует обертывания нового конструктора данных вокруг него (хотя он не должен быть GADT). - person mithrandi   schedule 28.03.2012[forall r. (forall b. Show b => b -> r) -> r]
- person Jeremy List   schedule 16.07.2016