Интерфейс Java и класс типа Haskell: различия и сходства?
пока я учусь Хаскелл, я заметил его класс, который, как предполагается, является великим изобретением, которое произошло от Haskell.
однако, в страница Википедии на типе class:
программист определяет класс типа путем указания набора функций или постоянные имена вместе с их соответствующими типами, которые должны существовать для каждого типа, который принадлежит классу.
который, кажется, скорее близко к интерфейс Java мне (цитирую интерфейс Википедии (Java) страница):
интерфейс на языке программирования Java является абстрактным типом, который используется для указания интерфейса (в общем смысле этого термина) что классы должны реализовать.
эти два вида довольно похожи: класс типа ограничивает поведение типа, а интерфейс ограничивает поведение класса.
интересно, в чем разница и сходства между классом типа в Haskell и интерфейсом в Java, или, может быть, они принципиально разные?
EDIT: Я заметил:даже haskell.org признает, что они похожи. Если они так похожи (или они?), тогда почему класс типа обрабатывается с такой шумихой?
ФАЙЛЫ: Вау, так много замечательных ответов! Я думаю, мне придется позволить сообществу решить, какой из них лучший. Однако, читая ответы, все они кажется, просто сказать, что "есть много вещей, которые typeclass может сделать, в то время как интерфейс не может или должен справиться с дженериками". Я не могу не задаться вопросом, есть ли что-нибудь интерфейсы могут делать в то время как typeclasses не может?может быть, вместо того, чтобы typeclass был похож на интерфейсы, его наоборот, что интерфейсы были под влиянием typeclass? есть ли какие-либо документы/документы, подтверждающие или опровергающие это? Спасибо за все ответы, они все очень поучительно!
Спасибо за все материалы!
10 ответов:
Я бы сказал, что интерфейс похож на класс типа
SomeInterface t
где все значения имеют типt -> whatever
(гдеwhatever
не содержитt
). Это связано с тем, что с типом отношения наследования в Java и подобных языках вызываемый метод зависит от типа объекта, на который они вызываются, и ничего больше.это означает, что это очень трудно сделать такие вещи, как
add :: t -> t -> t
с интерфейсом, где он полиморфен по нескольким параметрам, поскольку интерфейс не может указать, что тип аргумента и тип возвращаемого значения метода-это тот же тип, что и тип объекта, на который он вызывается (т. е. тип "self"). С дженериками есть своего рода способы подделать это, сделав интерфейс с общим параметром, который, как ожидается, будет того же типа, что и сам объект, например howComparable<T>
это, где вы должны использоватьFoo implements Comparable<Foo>
так чтоcompareTo(T otherobject)
что-то типаt -> t -> Ordering
. Но для этого все равно требуется программист следуйте этому правилу, а также вызывает головные боли, когда люди хотят сделать функцию, которая использует этот интерфейс, они должны иметь рекурсивные параметры универсального типа.кроме того, у вас не будет таких вещей, как
empty :: t
потому что вы не вызываете функцию здесь, так что это не метод.
что сходно между интерфейсами и классами типов, так это то, что они называют и описывают набор связанных операций. Сами операции описываются через их имена, входы и выходы. Точно так же может быть много реализаций этих операций, которые, вероятно, будут отличаться в их реализации.
с этим из пути, вот некоторые заметные различия:
- методы интерфейсов всегда связаны с экземпляром объекта. Иначе говоря, всегда существует подразумеваемый параметр "this", который является объектом, на котором вызывается метод. Все входные данные для функции класса типа являются явными.
- реализация интерфейса должна быть определена как часть класса, который реализует интерфейс. И наоборот, класс типа "экземпляр" может быть определен полностью отдельно от связанного с ним типа...даже в другом модуле.
- тип класса позволяет определить реализацию по умолчанию для любой из заданных операций. Интерфейсы-это только спецификации типа, без реализации.
В общем, я думаю, что справедливо сказать, что классы типов являются более мощными и гибкими, чем интерфейсы. Как бы вы определили интерфейс для преобразования строки в некоторое значение или экземпляр типа реализации? Это, конечно, не невозможно, но результат не будет интуитивным или элегантным. Вы когда-нибудь хотели, чтобы можно было реализовать интерфейс для типа в какой-то скомпилированной библиотеке? Это оба легко выполнить с классами типов.
классы типов были созданы как структурированный способ выражения "специального полиморфизма", который в основном является техническим термином для перегруженные функции. Определение класса выглядит примерно так:
class Foobar a where foo :: a -> a -> Bool bar :: String -> a
это означает, что при использовании применить функцию
foo
к некоторым аргументам типа, которые принадлежат классуFoobar
, он ищет реализациюfoo
специфические для этого типа, и использует это. Это очень похоже на ситуацию с перегрузкой оператора в таких языках, как C, за исключением более гибких и обобщенных.интерфейсы служат аналогичной цели в языках OO, но базовая концепция несколько отличается; языки OO поставляются со встроенным понятием иерархий типов, которых Haskell просто не имеет, что несколько усложняет дело, потому что интерфейсы могут включать в себя как перегрузку по подтипам (т. е. вызов методов на соответствующих экземплярах, подтипы, реализующие интерфейсы их супертипы do) и с помощью диспетчеризации на основе плоских типов (поскольку два класса, реализующие интерфейс, могут не иметь общего суперкласса, который также реализует его). Учитывая огромную дополнительную сложность, введенную подтипом, я предлагаю более полезно думать о классах типов как о улучшенной версии перегруженных функций на языке, отличном от OO.
также стоит отметить, что классы типов имеют значительно более гибкие средства отправки-интерфейсы обычно применяются только к одному классу, реализующему его, в то время как классы типов определяются на тип, который может появиться в любом месте сигнатуры функций класса. Эквивалент этого в интерфейсах OO позволит интерфейсу определять способы передачи объекта этого класса другим классам, определять статические методы и конструкторы, которые будут выбирать реализацию на основе what тип возвращаемого требуется в контексте вызова определить методы, которые принимают аргументы того же типа, что и класс, реализующий интерфейс, и различные другие вещи, которые на самом деле не переводить вообще.
короче говоря: они служат схожим целям, но способ их работы несколько отличается, и классы типов являются значительно более выразительными и, в некоторых случаях, более простыми в использовании из-за работы над фиксированными типами, а не частями иерархии наследования.
Я прочитал ответы выше. Я чувствую, что могу ответить чуть более четко:
"класс типа" Haskell и "интерфейс" Java/C# или "черта" Scala в основном аналогичны. Между ними нет концептуального различия, но есть различия в реализации:
- классы типа Haskell реализуются с помощью "экземпляров", которые отделены от определения типа данных. В C#/Java/Scala интерфейсы / признаки должны быть реализованы в классе определение.
- классы типа Haskell позволяют возвращать этот тип или тип self. Скала черт делать так же (это.тип.) Обратите внимание, что" self types " в Scala-это совершенно несвязанная функция. Java / C# требует грязного обходного пути с универсальными средствами для приближения этого поведения.
- классы типа Haskell позволяют определять функции (включая константы) без входного параметра типа "this". Интерфейсы Java/C# и черты Scala требуют" этого " входного параметра для всех функций.
- классы типа Haskell позволяют определять реализации по умолчанию для функций. Так же как и черты Scala и интерфейсы Java 8+. C# может приблизиться к чему-то вроде этого с помощью методов расширений.
смотрите выступление Филиппа Уодлера Вера, эволюция и языки программирования. Уодлер работал над Haskell и был крупным вкладчиком в Java Generics.
на мастерские умы программирования, есть интервью о Haskell с Филом Уодлером, изобретателем классов типов, которые объясняют сходство между интерфейсами в Java и классами типов в Haskell:
метод Java, как:
public static <T extends Comparable<T>> T min (T x, T y) { if (x.compare(y) < 0) return x; else return y; }
очень похоже на метод Хаскелла:
min :: Ord a => a -> a -> a min x y = if x < y then x else y
Итак, классы типов связаны с интерфейсами, но реальное соответствие будет статическим методом, параметризованным с помощью a типа как выше.
читать расширение программного обеспечения и интеграция с классами типов где приведены примеры того, как классы типов могут решить ряд проблем, которые интерфейсы не могут.
примеры, перечисленные в статье:
- проблема выражения,
- проблема интеграции фреймворка,
- проблема независимой расширяемости,
- тирания господствующего разложения, рассеяния и запутывание.
Я не могу говорить на уровне "шумихи", если это кажется таким прекрасным. Но да, классы типов похожи во многих отношениях. Одно отличие, о котором я могу думать, заключается в том, что Haskell вы можете предоставить поведение для некоторых классов типа операции:
class Eq a where (==), (/=) :: a -> a -> Bool x /= y = not (x == y) x == y = not (x /= y)
который показывает, что есть две операции, равной
(==)
, а не-равной(/=)
, для вещей, которые являются экземплярамиEq
тип класса. Но не равная операция определяется в терминах равных (так что ты только один), и наоборот.так что в вероятно-не-правовой-Java это будет что-то вроде:
interface Equal<T> { bool isEqual(T other) { return !isNotEqual(other); } bool isNotEqual(T other) { return !isEqual(other); } }
и способ, которым это будет работать, заключается в том, что вам нужно будет только предоставить один из этих методов для реализации интерфейса. Поэтому я бы сказал, что способность обеспечить своего рода частичную реализацию поведения, которое вы хотите в интерфейс уровень-это разница.
Они похожи (читай: имеют аналогичное использование) и, вероятно, реализованы аналогично: полиморфные функции в Haskell берут под капот "vtable", перечисляя функции, связанные с классом typeclass.
эта таблица часто может быть выведен во время компиляции. Это, вероятно, менее верно в Java.
но это таблица функции, а не методы. Методы привязаны к объекту, Haskell typeclasses-нет.
увидеть их скорее как дженерики Java.
как говорит Даниэль, реализации интерфейса определены отдельно от заявления. И как указывали другие, есть простой способ определить операции, которые используют один и тот же свободный тип в нескольких местах. Так что его легко определить
Num
Как typeclass. Таким образом, в Haskell мы получаем синтаксические преимущества перегрузки операторов без фактического наличия каких-либо магических перегруженных операторов-просто стандартные типовые объявления.еще одно отличие заключается в том, что вы можете использовать методы, основанные на типе, даже если у вас еще нет конкретного значения этого типа!
например,
read :: Read a => String -> a
. Поэтому, если у вас есть достаточно другой информации типа, висящей вокруг о том, как вы будете использовать результат "чтения", вы можете позволить компилятору выяснить, какой словарь использовать для вас.вы также можете делать вещи, как
instance (Read a) => Read [a] where...
что позволяет определить экземпляр чтения для любой список читаемых вещей. Я не думаю, что это вполне возможно в Java.и все это просто стандартные однопараметрические typeclasses без обмана происходит. Как только мы вводим многопараметрические классы типов, тогда открывается целый новый мир возможностей, и тем более с функциональными зависимостями и семействами типов, которые позволяют вам встраивать гораздо больше информации и вычислений в систему типов.