Интерфейс Java и класс типов Haskell: различия и сходства? - PullRequest
91 голосов
/ 05 августа 2011

Пока я изучал Haskell, я заметил класс , который, как предполагается, является великим изобретением, созданным на Haskell.

Однако в страница Википедии о классе типов :

Программист определяет класс типа, определяя набор функций или имена констант, вместе с их соответствующими типами, которые должны существовать для каждого типа, который принадлежит классу.

Что мне кажется довольно близким Интерфейс Java для меня (цитата Страница интерфейса Википедии (Java) ):

Интерфейс в языке программирования Java - это абстрактный тип, который используется для указания интерфейса (в общем смысле термина) что классы должны реализовывать.

Эти два выглядят довольно похоже: класс типов ограничивает поведение типа, а интерфейс ограничивает поведение класса.

Интересно, в чем различия и сходства между классом типов в Haskell и интерфейсом в Java, или, может быть, они принципиально отличаются?

РЕДАКТИРОВАТЬ: Я заметил , даже haskell.org признает, что они похожи . Если они так похожи (или они?), То почему класс типов трактуется с такой шумихой?

БОЛЬШЕ РЕДАКТИРОВАТЬ: Ух, так много хороших ответов! Думаю, мне придется позволить сообществу решить, какой из них лучший. Однако, читая ответы, все они, кажется, просто говорят, что «есть много вещей, которые может сделать класс типов, в то время как интерфейс не может или должен справляться с обобщениями» . Я не могу не задаться вопросом, есть ли что-нибудь, что могут сделать интерфейсы, в то время как классы типов не могут? Кроме того, я заметил, что Википедия утверждает, что класс типов был первоначально изобретен в газете 1989 года * "Как сделать специальный полиморфизм менее специальным ", в то время как Haskell все еще находится в своей колыбели, в то время как проект Java был начат в 1991 году и впервые выпущен в 1995 году. Так что может быть, вместо того, чтобы класс типов был похож на интерфейсы, наоборот, на интерфейсы влиял класс типов? Существуют ли какие-либо документы / документы, подтверждающие или опровергающие это? Спасибо за все ответы, все они очень поучительны!

Спасибо за все входные данные!

Ответы [ 10 ]

39 голосов
/ 05 августа 2011

Я бы сказал, что интерфейс похож на класс типов SomeInterface t, где все значения имеют тип t -> whatever (где whatever не содержит t).Это связано с тем, что с типом отношений наследования в Java и аналогичных языках вызываемый метод зависит от типа объекта, к которому они обращаются, и ничего более.

Это означает, что действительно трудно создавать такие вещи, как add :: t -> t -> t с интерфейсом, где он полиморфен для более чем одного параметра, потому что интерфейс не может указать, что тип аргумента и тип возврата метода совпадают с типом объекта, для которого он вызывается (т.е.тип "Я").В Generics есть несколько способов подделать это, создав интерфейс с универсальным параметром, который, как ожидается, будет того же типа, что и сам объект, например, как Comparable<T> делает это, где вы должны использовать Foo implements Comparable<Foo>, чтобыcompareTo(T otherobject) вид имеет тип t -> t -> Ordering.Но это все еще требует от программиста следовать этому правилу, а также вызывает головную боль, когда люди хотят создать функцию, использующую этот интерфейс, они должны иметь рекурсивные параметры универсального типа.

Кроме того, у вас не будет вещейкак empty :: t, потому что здесь вы не вызываете функцию, поэтому это не метод.

38 голосов
/ 05 августа 2011

Что сходно между интерфейсами и классами типов, так это то, что они называют и описывают набор связанных операций.Сами операции описываются через их имена, входы и выходы.Точно так же может быть много реализаций этих операций, которые, вероятно, будут отличаться по своей реализации.

С этим, между прочим, есть некоторые заметные различия:

  • Методы интерфейсов всегда связаныс экземпляром объекта.Другими словами, всегда существует подразумеваемый параметр this, который является объектом, для которого вызывается метод.Все входные данные для функции класса типа являются явными.
  • Реализация интерфейса должна быть определена как часть класса, который реализует интерфейс.И наоборот, класс типа «экземпляр» может быть определен полностью отдельно от его ассоциированного типа ... даже в другом модуле.
  • Класс типа позволяет вам определять реализацию «по умолчанию» для любой из определенных операций.Интерфейсы являются строго спецификациями типов, без реализации.

В целом, я считаю справедливым сказать, что классы типов более мощные и гибкие, чем интерфейсы.Как бы вы определили интерфейс для преобразования строки в какое-то значение или экземпляр реализующего типа?Это, конечно, не невозможно, но результат не будет интуитивным или элегантным.Вы когда-нибудь хотели реализовать интерфейс для типа в некоторой скомпилированной библиотеке?Это легко сделать с помощью классов типов.

21 голосов
/ 05 августа 2011

Классы типов были созданы как структурированный способ выражения "специального полиморфизма", который в основном является техническим термином для перегруженных функций .Определение класса типа выглядит примерно так:

class Foobar a where
    foo :: a -> a -> Bool
    bar :: String -> a

Это означает, что когда вы применяете функцию foo к некоторым аргументам типа, принадлежащим классу Foobar, оно выглядитдо реализации foo, специфичной для этого типа, и использует ее.Это очень похоже на ситуацию с перегрузкой операторов в таких языках, как C ++ / C #, за исключением более гибких и обобщенных.

Интерфейсы служат аналогичной цели в языках OO, но основная концепция несколько отличается;ОО-языки поставляются со встроенным понятием иерархии типов, которого у Haskell просто нет, что несколько усложняет ситуацию, потому что интерфейсы могут включать как перегрузку с помощью подтипов (то есть, вызывая методы в соответствующих экземплярах, так и подтипы, реализующие интерфейсы, которые делают их супертипы)и посредством плоской диспетчеризации на основе типов (поскольку два класса, реализующие интерфейс, могут не иметь общего суперкласса, который также реализует его).Учитывая огромную дополнительную сложность, связанную с подтипами, я предлагаю более полезно рассматривать классы типов как улучшенную версию перегруженных функций на языке, отличном от OO.

Также стоит отметить, что классы типов обладают гораздо большей гибкостью.средства диспетчеризации - интерфейсы обычно применяются только к одному реализующему его классу, тогда как классы типов определены для типа , который может присутствовать где угодно в сигнатуре функций класса.Эквивалент этого в интерфейсах OO позволит интерфейсу определять способы передачи объекта этого класса в другие классы, определять статические методы и конструкторы, которые будут выбирать реализацию на основе того, что тип возвращаемого значения требуется ввызывая контекст, определяйте методы, которые принимают аргументы того же типа, что и класс, реализующий интерфейс, и различные другие вещи, которые на самом деле не переводятся вообще.

Короче говоря: они служат аналогичным целям, но так, как ониработа несколько отличается, и классы типов значительно более выразительны, а в некоторых случаях их проще использовать из-за работы с фиксированными типами, а не из частей иерархии наследования.

12 голосов
/ 05 сентября 2014

Я прочитал ответы выше.Я чувствую, что могу ответить чуть более четко:

"Класс" типа Haskell и "интерфейс" Java / C # или "черта" Scala в основном аналогичны.Между ними нет концептуального различия, но есть различия в реализации:

  • Классы типа Haskell реализованы с "экземплярами", которые отделены от определения типа данных.В C # / Java / Scala интерфейсы / черты должны быть реализованы в определении класса.
  • Классы типов Haskell позволяют вам возвращать этот тип или собственный тип.Черты Scala также хороши (this.type).Обратите внимание, что «типы самости» в Scala - это совершенно не связанная функция.Java / C # требует грязного обходного пути с обобщениями для аппроксимации этого поведения.
  • Классы типа Haskell позволяют определять функции (включая константы) без входного параметра типа "this".Интерфейсы Java / C # и черты Scala требуют входной параметр "this" для всех функций.
  • Классы типов Haskell позволяют вам определять реализации по умолчанию для функций.То же самое можно сказать о чертах Scala и интерфейсах Java 8+.C # может аппроксимировать что-то подобное с помощью методов расширений.
9 голосов
/ 14 августа 2011

Смотрите доклад Филиппа Уодлера Языки веры, эволюции и программирования .Вадлер работал над Haskell и был одним из основных разработчиков Java Generics.

8 голосов
/ 20 января 2014

В Главные умы программирования , есть интервью о Хаскеле с Филом Уодлером, изобретателем классов типов, который объясняет сходство между интерфейсами в 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; 
   }

очень похож на метод Haskell:

   min :: Ord a => a -> a -> a
   min x y  = if x < y then x else y

Итак, классы типов связаны с интерфейсами, но реальным соответствием будет статический метод, параметризованный с типом, как указано выше.

8 голосов
/ 15 августа 2011

Чтение Расширение программного обеспечения и интеграция с классами типов , где приводятся примеры того, как классы типов могут решать ряд проблем, которые не могут выполнять интерфейсы.

Примеры, перечисленные в документе:

  • проблема выражения,
  • проблема интеграции структуры,
  • проблема независимой расширяемости,
  • тирания доминирующего разложения, рассеяния и спутывания.
5 голосов
/ 05 августа 2011

Я не могу говорить с уровнем "шумихи", если это кажется таким хорошим. Но да, классы типов во многом похожи. Одно из различий, о которых я могу подумать, заключается в том, что с помощью 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); 
    }
}

и способ, которым он будет работать, заключается в том, что вам нужно будет предоставить только один из этих методов для реализации интерфейса. Поэтому я бы сказал, что возможность обеспечить своего рода частичную реализацию желаемого поведения на уровне interface является разницей.

4 голосов
/ 05 августа 2011

Они похожи (читай: имеют похожее использование) и, вероятно, реализованы аналогично: полиморфные функции в Haskell получают «капюшон», перечисляющий функции, связанные с классом типов.

Эта таблица часто может быть выведена во время компиляции.Это, вероятно, менее верно в Java.

Но это таблица функций , а не методов .Методы привязаны к объекту, классы типов Haskell - нет.

Видите их скорее как дженерики Java.

3 голосов
/ 05 августа 2011

Как говорит Даниэль, реализации интерфейса определяются отдельно из объявлений данных.И, как уже отмечали другие, существует простой способ определения операций, использующих один и тот же свободный тип в нескольких местах.Так что легко определить Num как класс типов.Таким образом, в Haskell мы получаем синтаксические преимущества перегрузки операторов, фактически не имея магически перегруженных операторов - просто стандартные классы типов.

Другое отличие состоит в том, что вы можете использовать методы, основанные на типе, даже если у васконкретное значение этого типа еще висит вокруг!

Например, read :: Read a => String -> a.Так что, если у вас достаточно информации о типах, которые вы используете, о том, как вы будете использовать результат «чтения», вы можете позволить компилятору выяснить, какой словарь использовать для вас.

Вы также можете делать такие вещи, как instance (Read a) => Read [a] where..., который позволяет вам определять экземпляр для чтения для любого списка читаемых вещей.Я не думаю, что это вполне возможно в Java.

И все это - просто стандартные классы типов с одним параметром без хитрости.Как только мы представим многопараметрические классы типов, откроется целый новый мир возможностей, и тем более с функциональными зависимостями и семействами типов, которые позволяют встраивать гораздо больше информации и вычислений в систему типов.

...