Типовой класс - Type class
В Информатика, а тип класс это система типов конструкция, поддерживающая специальный полиморфизм. Это достигается путем добавления ограничений на тип переменных в параметрически полиморфный типы. Такое ограничение обычно включает класс типа Т
и переменная типа а
, и означает, что а
может быть создан только для типа, члены которого поддерживают перегруженные операции, связанные с Т
.
Типовые классы были впервые реализованы в Язык программирования Haskell после первого предложения Филип Вадлер и Стивен Блотт как расширение "eqtypes" в Стандартный ML,[1][2] и изначально задумывались как способ реализации перегруженные арифметические операторы и операторы равенства принципиальным образом.[3][4]В отличие от «eqtypes» Standard ML, перегрузка оператора равенства посредством использования классов типов в Haskell не требует значительных изменений внешнего интерфейса компилятора или базовой системы типов.[5]
С момента их создания было обнаружено множество других приложений классов типов.
Обзор
Классы типов определяются путем указания набора имен функций или констант вместе с соответствующими типами, которые должны существовать для каждого типа, принадлежащего этому классу. В Haskell типы можно параметризовать; типовой класс Уравнение
предназначенные для содержания типов, допускающих равенство, будут объявлены следующим образом:
учебный класс Уравнение а куда (==) :: а -> а -> Bool (/=) :: а -> а -> Bool
куда а
является одним из экземпляров класса типа Уравнение
, и а
определяет сигнатуры функций для 2 функций (функции равенства и неравенства), каждая из которых принимает 2 аргумента типа а
и верните логическое значение.
Переменная типа а
имеет своего рода (также известный как Тип
в последнем GHC релиз),[6] это означает, что вид Уравнение
является
Уравнение :: Тип -> Ограничение
Объявление может быть прочитано как указание типа а
принадлежит к классу типов Уравнение
если есть функции с именем (==)
, и (/=)
, соответствующих типов, определенных на нем ". Затем программист может определить функцию элем
(который определяет, находится ли элемент в списке) следующим образом:
элем :: Уравнение а => а -> [а] -> Boolэлем у [] = Ложьэлем у (Икс:хз) = (Икс == у) || элем у хз
Функция элем
имеет тип a -> [a] -> Bool
с контекстом Уравнение а
, что ограничивает типы, которые а
может простираться до тех а
которые принадлежат Уравнение
тип класс. (Примечание: Haskell =>
можно назвать «ограничением класса».)
Программист может сделать любой тип т
член данного класса типа C
используя объявление экземпляра который определяет реализации всех C
методы для конкретного типа т
. Например, если программист определяет новый тип данных т
, они могут затем сделать этот новый тип экземпляром Уравнение
путем предоставления функции равенства значений типа т
любым способом, который они считают нужным. Как только они это сделают, они могут использовать функцию элем
на [т]
, то есть списки элементов типа т
.
Обратите внимание, что классы типов отличаются от классы в объектно-ориентированных языках программирования. Особенно, Уравнение
не тип: нет такой вещи, как ценить типа Уравнение
.
Классы типов тесно связаны с параметрическим полиморфизмом. Например, обратите внимание, что тип элем
как указано выше, будет параметрически полиморфным типом a -> [a] -> Bool
если бы не ограничение класса типа "Уравнение а =>
".
Высокодородный полиморфизм
Класс типа не должен принимать переменную типа своего рода Тип
но можно взять любой. Эти классы типов с более высокими типами иногда называют классами конструкторов (упомянутые конструкторы являются конструкторами типов, такими как Может быть
, а не конструкторы данных, такие как Только
). Примером может служить Монада
учебный класс:
учебный класс Монада м куда возвращаться :: а -> м а (>>=) :: м а -> (а -> м б) -> м б
Тот факт, что m применяется к переменной типа, указывает, что она имеет вид Тип -> Тип
, т.е. он принимает тип и возвращает тип, вид Монада
таким образом:
Монада :: (Тип -> Тип) -> Ограничение
Классы многопараметрических типов
Классы типов допускают несколько параметров типа, поэтому классы типов можно рассматривать как отношения между типами.[7] Например, в GHC стандартная библиотека, класс IArray
выражает общий неизменяемый интерфейс массива. В этом классе ограничение класса типа IArray a e
Значит это а
- это тип массива, содержащий элементы типа е
. (Это ограничение полиморфизма используется для реализации распакованный типы массивов, например.)
Нравиться мультиметоды[нужна цитата ]классы типов с несколькими параметрами поддерживают вызов различных реализаций метода в зависимости от типов нескольких аргументов и действительно возвращаемых типов. Классы с несколькими параметрами не требуют поиска метода для вызова при каждом вызове во время выполнения;[8]:минута 25:12 скорее вызываемый метод сначала компилируется и сохраняется в словаре экземпляра класса типа, как и в случае с классами типов с одним параметром.
Код Haskell, использующий классы типов с несколькими параметрами, непереносим, поскольку эта возможность не является частью стандарта Haskell 98. Популярные реализации Haskell, GHC и Объятия, поддерживают классы с несколькими параметрами.
Функциональные зависимости
В Haskell классы типов были усовершенствованы, чтобы позволить программисту объявлять функциональные зависимости между параметрами типа - концепция вдохновлен теорией реляционных баз данных.[9][10] То есть программист может утверждать, что данное присвоение некоторого подмножества параметров типа однозначно определяет остальные параметры типа. Например, генерал монады м
которые несут параметр состояния типа s
удовлетворить ограничение класса типа Monad.State s m
. В этом ограничении есть функциональная зависимость м -> с
. Это означает, что для данной монады м
типового класса Monad.State
, тип состояния, доступный из м
определяется однозначно. Это помогает компилятору в вывод типа, а также помощь программисту в типо-ориентированное программирование.
Саймон Пейтон-Джонс возражает против введения функциональных зависимостей в Haskell по причине сложности.[11]
Классы типов и неявные параметры
Классы типов и неявные параметры очень похожи по своей природе, хотя и не совсем одинаковы. Полиморфная функция с ограничением класса типа, например:
сумма :: Num а => [а] -> а
можно интуитивно рассматривать как функцию, которая неявно принимает экземпляр Num
:
сумма_ :: Num_ а -> [а] -> а
Экземпляр Num_ a
по сути, это запись, содержащая определение экземпляра Num a
. (Фактически, именно так классы типов реализуются под капотом компилятора Glasgow Haskell.)
Однако есть принципиальное отличие: неявные параметры больше гибкий - вы можете передавать разные экземпляры Num Int
. Напротив, классы типов применяют так называемые согласованность свойство, которое требует, чтобы был только один уникальный выбор экземпляра для любого данного типа. Свойство coherence делает классы типов в некоторой степени антимодульными, поэтому настоятельно не рекомендуется использовать бесхозные экземпляры (экземпляры, которые определены в модуле, который не содержит ни класс, ни интересующий тип). С другой стороны, согласованность добавляет языку дополнительный уровень безопасности, обеспечивая программисту гарантию того, что две непересекающиеся части одного и того же кода будут совместно использовать один и тот же экземпляр.[12]
Например, заказанный набор (типа Установить
) требует общий заказ на элементах (типа а
) для того, чтобы функционировать. Об этом может свидетельствовать ограничение Ord a
, который определяет оператор сравнения для элементов. Однако существует множество способов навести тотальный порядок. Поскольку алгоритмы набора обычно нетерпимы к изменениям в порядке после создания набора, передача несовместимого экземпляра Ord a
к функциям, которые работают на устройстве, может привести к неверным результатам (или сбоям). Таким образом, обеспечение согласованности Ord a
в этом конкретном сценарии имеет решающее значение.
Экземпляры (или "словари") в Scala классы типов - это просто обычные значения в языке, а не совершенно отдельный вид сущности.[13][14] Хотя эти экземпляры по умолчанию предоставляются путем нахождения подходящих экземпляров в области видимости для использования в качестве неявных фактических параметров для явно объявленных неявных формальных параметров, тот факт, что они являются обычными значениями, означает, что они могут быть предоставлены явно для устранения неоднозначности. В результате классы типов Scala не удовлетворяют свойству согласованности и фактически являются синтаксическим сахаром для неявных параметров.
Это пример из книги Кошек. [15] документация:
// Класс типа для текстового представлениячерта Показать[А] { def Показать(ж: А): Нить}// Полиморфная функция, которая работает только при неявном // экземпляр Show [A] доступенdef бревно[А](а: А)(скрытый s: Показать[А]) = println(s.Показать(а))// Экземпляр для Stringскрытый вал stringShow = новый Показать[Нить] { def Показать(s: Нить) = s}// Параметр stringShow был вставлен компилятором.Scala> бревно("строка")а нить
Coq (версия 8.2 и новее) также поддерживает классы типов путем вывода соответствующих экземпляров.[16] Последние версии Агда 2 также предоставляют аналогичную функцию, называемую «аргументами экземпляра».[17]
Другие подходы к перегрузке операторов
В Стандартный ML, механизм «типов равенства» примерно соответствует классу встроенных типов Haskell Уравнение
, но все операторы равенства выводятся компилятором автоматически. Контроль программиста над процессом ограничивается указанием того, какие компоненты типа в структуре являются типами равенства, а какие переменные типа в диапазоне полиморфных типов над типами равенства.
SML и OCaml модули и функторы могут играть роль, аналогичную роли классов типов Haskell, основное отличие заключается в роли вывода типов, что делает классы типов подходящими для для этого случая полиморфизм.[18]Объектно-ориентированное подмножество OCaml это еще один подход, который несколько сравним с подходом классов типов.
Связанные понятия
Аналогичное понятие для перегруженных данных (реализовано в GHC ) принадлежит тип семьи.[19]
В Чистый классы типов похожи на Haskell, но имеют немного другой синтаксис.
Ржавчина поддерживает черты, которые представляют собой ограниченную форму классов типов с согласованностью.[20]
Меркурий имеет классы типов, хотя они не совсем такие, как в Haskell.[требуется дальнейшее объяснение ]
В Scala, классы типов - это идиома программирования который может быть реализован с помощью существующих языковых функций, таких как неявные параметры, а не отдельной языковой функции как таковой. Благодаря тому, как они реализованы в Scala, можно явно указать, какой экземпляр класса типа использовать для типа в конкретном месте кода в случае двусмысленности. Однако это не обязательно является преимуществом, поскольку экземпляры классов неоднозначного типа могут быть подвержены ошибкам.
Помощник доказательства Coq также поддерживает классы типов в последних версиях. В отличие от обычных языков программирования, в Coq любые законы класса типов (например, законы монад), которые изложены в определении класса типа, должны быть математически подтверждены для каждого экземпляра класса типа перед их использованием.
Смотрите также
- Полиморфизм (информатика) (другие виды полиморфизма)
- Язык программирования Haskell (язык, на котором впервые были разработаны классы типов)
- Перегрузка оператора (одно приложение классов типов)
- Монада (функциональное программирование) (
Монада
является примером типового класса) - Концепции (C ++) (начиная с C ++ 20)
- Rust (язык программирования)
Рекомендации
- ^ Моррис, Джон (2013). «Классы типов и цепочки экземпляров» (PDF).
- ^ Вадлер, Филипп (октябрь 1988 г.). "Как сделать специальный полиморфизм менее случайным".
- ^ Каес, Стефан (март 1988 г.). «Параметрическая перегрузка в полиморфных языках программирования». Proc. 2-й Европейский симпозиум по языкам программирования. Дои:10.1007/3-540-19027-9_9.
- ^ Вадлер, Филипп; Стивен Блотт (январь 1989 г.). "Как сделать специальный полиморфизм менее случайным". Proc. 16-й симпозиум ACM по принципам языков программирования.
- ^ Аппель, Эндрю; Дэвид Маккуин (июнь 1991 г.). "Стандартный ML Нью-Джерси". Proc. 3-й Международный симпозиум по реализации языков программирования и логическому программированию.
- ^
Тип
изData.Kind
появился в версии 8 Компилятор Glasgow Haskell - ^ Haskell ' страница MultiParamTypeClasses.
- ^ В GHC ядро C использует сигнатуры типа System F от Girard & Reynold для определения типизированного случая для обработки на этапах оптимизации. - Саймон Пейтон-Джонс "В ядро - сжатие Haskell до девяти конструкторов » Конференция пользователей Erlang, 14 сентября 2016 г.
- ^ Марк Джонс. Классы типов с функциональными зависимостями. Из Proc. 9-й Европейский симпозиум по программированию. Март 2000 г.
- ^ Haskell ' страница Функциональные зависимости.
- ^ http://www.haskell.org/pipermail/haskell-prime/2006-Feb February/000289.html
- ^ Эдвард Кметт, Типовые классы против мира, Встреча сообщества Boston Haskell.
- ^ Оливейра, Бруно; Адриан Мурс; Мартин Одерский (2010). «Классы типов как объекты и следствия» (PDF). OOPSLA.
- ^ "Руководство по Scala для неофита, часть 12: Типовые классы - Дэниел Вестхайд".
- ^ typelevel.org, Scala Cats
- ^ Нежное введение в классы типов и отношения в Coq
- ^ "Классы типов моделирования с аргументами экземпляра ".
- ^ Дрейер, Дерек; Роберт Харпер; Мануэль М.Т. Чакраварти (апрель 2006 г.). «Классы модульного типа». Цитировать журнал требует
| журнал =
(помощь) - ^ "GHC / Семейства типов - HaskellWiki".
- ^ «Специализация, согласованность и эволюция API · Аарон Турон».
- Саймон Пейтон Джонс, Марк Джонс, Эрик Мейер. Типовые классы: исследование пространства дизайна. Из Proc. ACM SIGPLAN Мастерская Haskell. Май 1997 г.
внешняя ссылка
- Нежное введение в Haskell, версия 98, глава 5. Классы типов и перегрузка. Июнь 2000 г.
- Курс расширенного функционального программирования в Утрехтском университете, 74 слайда лекций на Расширенные классы типов. 2005-06-07.
- Реализация и понимание классов типов. 2014-11-13.