Пока я изучаю Haskell, я обратил внимание на его типовой класс , который, как предполагается, был великим изобретением, появившимся на Haskell.
Однако на странице Википедии о классе типов :
Программист определяет класс типа, указывая набор имен функций или констант вместе с их соответствующими типами, которые должны существовать для каждого типа, принадлежащего этому классу.
Что мне кажется довольно близким к интерфейсу Java (цитируя страницу Интерфейса Википедии (Java) ):
Интерфейс в языке программирования Java - это абстрактный тип, который используется для определения интерфейса (в общем смысле этого термина), который классы должны реализовать.
Эти два выглядят довольно похоже: класс типа ограничивает поведение типа, а интерфейс ограничивает поведение класса.
Интересно, в чем разница и сходство между классом типов в Haskell и интерфейсом в Java, или, может быть, они принципиально разные?
EDIT: я заметил, что даже haskell.org признает, что они похожи . Если они так похожи (или таковы?), То почему класс типов вызывает такую шумиху?
БОЛЬШЕ РЕДАКТИРОВАНИЯ: Вау, так много отличных ответов! Думаю, мне придется позволить сообществу решить, какой из них лучший. Однако, читая ответы, все они, кажется, просто говорят, что «класс типов может многое сделать, в то время как интерфейс не может или должен справляться с дженериками» . Я не могу не задаться вопросом, могут ли интерфейсы что-нибудь делать, а классы типов - нет? Кроме того, я заметил, что Википедия утверждает, что класс типов был первоначально изобретен в статье 1989 года * «Как сделать специальный полиморфизм менее специальным», в то время как Haskell все еще находится в колыбели, а проект Java был начат в 1991 году и впервые выпущен в 1995 году. Так что, может быть, вместо того, чтобы класс типов был похож на интерфейсы, а наоборот, на интерфейсы повлиял класс типов?Есть ли какие-то документы / документы, подтверждающие или опровергающие это? Спасибо за все ответы, все они очень полезны!
Спасибо за вклад!
Ответы:
Я бы сказал, что интерфейс похож на класс типа,
SomeInterface t
где все значения имеют типt -> whatever
(гдеwhatever
не содержитt
). Это связано с тем, что с типом отношения наследования в Java и подобных языках вызываемый метод зависит от типа объекта, для которого они вызваны, и ничего больше.Это означает, что очень сложно сделать что-то вроде
add :: t -> t -> t
интерфейса, где он полиморфен более чем по одному параметру, потому что у интерфейса нет способа указать, что тип аргумента и тип возвращаемого значения метода того же типа, что и тип объект, для которого он вызывается (то есть тип "self"). С помощью Generics есть своего рода способы подделать это, создав интерфейс с универсальным параметром, который, как ожидается, будет того же типа, что и сам объект, например, какComparable<T>
это происходит, где вы должны использовать,Foo implements Comparable<Foo>
чтобыcompareTo(T otherobject)
тип имел типt -> t -> Ordering
. Но это по-прежнему требует от программиста следовать этому правилу, а также вызывает головную боль, когда люди хотят создать функцию, использующую этот интерфейс, они должны иметь параметры рекурсивного универсального типа.Кроме того, у вас не будет таких вещей, как
empty :: t
потому, что вы здесь не вызываете функцию, поэтому это не метод.источник
Что общего между интерфейсами и классами типов, так это тем, что они называют и описывают набор связанных операций. Сами операции описываются через их имена, входы и выходы. Точно так же может существовать множество реализаций этих операций, которые, вероятно, будут различаться по своей реализации.
Итак, вот некоторые заметные отличия:
В общем, я считаю справедливым сказать, что классы типов более мощные и гибкие, чем интерфейсы. Как бы вы определили интерфейс для преобразования строки в какое-то значение или экземпляр реализующего типа? Конечно, это не невозможно, но результат не будет интуитивно понятным или элегантным. Вы когда-нибудь мечтали о возможности реализовать интерфейс для типа в какой-нибудь скомпилированной библиотеке? И то, и другое легко выполнить с помощью классов типов.
источник
Классы типов были созданы как структурированный способ выражения «специального полиморфизма», который в основном является техническим термином для перегруженных функций . Определение класса типа выглядит примерно так:
Это означает, что когда вы используете функцию apply
foo
к некоторым аргументам типа, принадлежащего классуFoobar
, она ищет реализацию,foo
специфичную для этого типа, и использует ее. Это очень похоже на ситуацию с перегрузкой операторов в таких языках, как C ++ / C #, за исключением более гибких и обобщенных.Интерфейсы служат той же цели в объектно-ориентированных языках, но основная концепция несколько отличается; ОО-языки имеют встроенное понятие иерархии типов, которого просто нет в Haskell, что в некоторой степени усложняет ситуацию, потому что интерфейсы могут включать в себя как перегрузку по подтипам (т.е. вызов методов в соответствующих экземплярах, подтипы, реализующие интерфейсы, выполняемые их супертипами) и с помощью диспетчеризации на основе плоского типа (поскольку два класса, реализующие интерфейс, могут не иметь общего суперкласса, который также его реализует). Учитывая огромную дополнительную сложность, вносимую подтипами, я предлагаю более полезным думать о классах типов как о улучшенной версии перегруженных функций на языке, отличном от объектно-ориентированного программирования.
Также стоит отметить , что классы типа имеют гораздо более гибкие средства отправки - интерфейсы обычно применяются только к одному классу его реализации, в то время как классы типов определены для типа , который может появиться в любом месте в сигнатуре функций класса. Эквивалент этого в интерфейсах объектно-ориентированных приложений мог бы позволить интерфейсу определять способы передачи объекта этого класса другим классам, определять статические методы и конструкторы, которые выбирали бы реализацию на основе того, какой тип возвращаемого значения требуется в контексте вызова, определять методы, которые принимать аргументы того же типа, что и класс, реализующий интерфейс, и различные другие вещи, которые на самом деле вообще не переводятся.
Вкратце: они служат схожим целям, но способ их работы несколько отличается, а классы типов значительно более выразительны и, в некоторых случаях, проще в использовании из-за работы с фиксированными типами, а не с частями иерархии наследования.
источник
Я прочитал ответы выше. Я чувствую, что могу ответить немного более четко:
«Класс типов» Haskell и «интерфейс» Java / C # или «черта» Scala в основном аналогичны. Между ними нет концептуальных различий, но есть различия в реализации:
источник
В Master of Programming есть интервью о Haskell с Филом Вадлером, изобретателем классов типов, который объясняет сходство между интерфейсами в Java и классами типов в Haskell:
Итак, классы типов связаны с интерфейсами, но реальным соответствием будет статический метод, параметризованный типом, как указано выше.
источник
Посмотрите доклад Филипа Вадлера « Вера, эволюция и языки программирования» . Вадлер работал над Haskell и был одним из основных разработчиков Java Generics.
источник
Прочтите « Расширение программного обеспечения и интеграция с классами типов», где приведены примеры того, как классы типов могут решить ряд проблем, которые не могут решить интерфейсы.
Примеры, перечисленные в документе:
источник
Я не могу говорить на уровне "ажиотажа", если мне так кажется. Но да, классы типов во многом похожи. Одна разница , что я могу думать о том , что Haskell вы можете обеспечить поведение для некоторых из класса типов в операциях :
который показывает, что есть две операции, равные
(==)
и не равные(/=)
, для вещей, которые являются экземплярамиEq
класса типов. Но операция неравенства определяется в терминах равенства (так что вам нужно будет указать только одно), и наоборот.Таким образом, в вероятно-незаконной-Java это будет примерно так:
и способ, которым это будет работать, заключается в том, что вам нужно будет только предоставить один из этих методов для реализации интерфейса. Поэтому я бы сказал, что возможность обеспечить своего рода частичную реализацию желаемого поведения на уровне интерфейса - это разница.
источник
Они похожи (читай: имеют аналогичное использование) и, вероятно, реализованы аналогичным образом: полиморфные функции в Haskell берут под капот «vtable», в котором перечислены функции, связанные с классом типов.
Эта таблица часто может быть выведена во время компиляции. Вероятно, это не так в Java.
Но это таблица функций , а не методов . Методы привязаны к объекту, классы типов Haskell - нет.
Считайте их похожими на дженерики Java.
источник
Как говорит Дэниел, реализации интерфейса определяются отдельно от объявлений данных. И, как указывали другие, существует простой способ определить операции, которые используют один и тот же свободный тип более чем в одном месте. Так что его легко определить
Num
как класс типов. Таким образом, в Haskell мы получаем синтаксические преимущества перегрузки операторов, фактически не имея никаких магических перегруженных операторов - только стандартные классы типов.Еще одно отличие состоит в том, что вы можете использовать методы, основанные на типе, даже если у вас еще нет конкретного значения этого типа!
Например,
read :: Read a => String -> a
. Так что, если у вас есть достаточно информации о другом типе о том, как вы будете использовать результат «чтения», вы можете позволить компилятору выяснить, какой словарь использовать для вас.Вы также можете делать такие вещи, как
instance (Read a) => Read [a] where...
определение экземпляра чтения для любого списка читаемых вещей. Я не думаю, что это возможно в Java.И все это просто стандартные однопараметрические классы типов без каких-либо ухищрений. Как только мы введем многопараметрические классы типов, откроется целый новый мир возможностей, и тем более с функциональными зависимостями и семействами типов, которые позволят вам встраивать гораздо больше информации и вычислений в систему типов.
источник