Чи формально система типу Haskell еквівалентна Java? [зачинено]


66

Я усвідомлюю, що деякі речі легші / складніші в одній мові, ніж інші, але мене цікавлять лише функції, пов'язані з типом, які можливі в одній, а в іншій неможливі / неактуальні. Щоб зробити його більш конкретним, давайте ігноруємо розширення типу Haskell, оскільки там так багато, що роблять усілякі шалені / круті речі.


4
Мені теж цікаво почути відповіді на це запитання теоретиків довгого звиття; хоча я сумніваюся, що це особливо зрозумію, я все ще зацікавлений у деталізації цього питання. Моя схильність від речей , які я прочитав, що система типу HM дозволяє компілятору знати тонну про те, що робить ваш код, тому він здатний виводячи типи так багато, а також дають стількох гарантій щодо поведінки. Але це лише мій інстинкт кишки, і я впевнений, що є інші речі, про які я абсолютно не знаю.
Джиммі Хоффа

1
Це чудове запитання - час підправити це для послідовників великої дискусії про Haskell / JVM!
Martijn Verburg

6
@ m3th0dman: Scala має таку ж підтримку функцій вищого порядку, як і у Java. У Scala функції першого класу просто представлені як екземпляри абстрактних класів з одним абстрактним методом, як і Java. Звичайно, у Scala є синтаксичний цукор для визначення цих функцій, і він має багату стандартну бібліотеку як заздалегідь визначених типів функцій, так і методів, які приймають функції, але з точки зору системного типу , що стосується цього питання, немає різниці . Отже, якщо Scala може це зробити, то і Java теж може, а насправді є Java-натхненні Haskell FP-бібліотеки для Java.
Йорг W Міттаг

2
@ m3th0dman: Це не про це питання.
Філ

7
@ m3th0dman Вони абсолютно звичайні типи. У списках немає нічого особливого, окрім деяких синактичних смаків. Ви можете легко визначити свій власний алгебраїчний тип даних, еквівалентний вбудованому типу списку, за винятком буквального синтаксису та імен конструкторів.
sepp2k

Відповіді:


63

("Java", як тут використовується, визначається як стандартний Java SE 7 ; "Haskell", як тут використовується, визначається як стандарт Haskell 2010. )

Те, що є в системі типу Java, але у Haskell немає:

  • номінальний поліморфізм підтипу
  • інформація про часткове виконання

Те, що має система типу Haskell, але те, що у Java немає:

  • обмежений спеціальний поліморфізм
    • породжує поліморфізм підтипу на основі обмежень
  • параметричний поліморфізм вищого роду
  • головне введення тексту

Редагувати:

Приклади кожного з перерахованих вище пунктів:

Унікальний для Java (порівняно з Haskell)

Поліморфізм номінального підтипу

/* declare explicit subtypes (limited multiple inheritance is allowed) */
abstract class MyList extends AbstractList<String> implements RandomAccess {

    /* specify a type's additional initialization requirements */
    public MyList(elem1: String) {
        super() /* explicit call to a supertype's implementation */
        this.add(elem1) /* might be overridden in a subtype of this type */
    }

}

/* use a type as one of its supertypes (implicit upcasting) */
List<String> l = new ArrayList<>() /* some inference is available for generics */

Часткова інформація про час виконання

/* find the outermost actual type of a value at runtime */
Class<?> c = l.getClass // will be 'java.util.ArrayList'

/* query the relationship between runtime and compile-time types */
Boolean b = l instanceOf MyList // will be 'false'

Унікальний для Haskell (порівняно з Java)

Обмежений спеціальним поліморфізмом

-- declare a parametrized bound
class A t where
  -- provide a function via this bound
  tInt :: t Int
  -- require other bounds within the functions provided by this bound
  mtInt :: Monad m => m (t Int)
  mtInt = return tInt -- define bound-provided functions via other bound-provided functions

-- fullfill a bound
instance A Maybe where
  tInt = Just 5
  mtInt = return Nothing -- override defaults

-- require exactly the bounds you need (ideally)
tString :: (Functor t, A t) => t String
tString = fmap show tInt -- use bounds that are implied by a concrete type (e.g., "Show Int")

Поліморфізм підтипу на основі обмежень (заснований на обмеженому спеціальному поліморфізмі)

-- declare that a bound implies other bounds (introduce a subbound)
class (A t, Applicative t) => B t where -- bounds don't have to provide functions

-- use multiple bounds (intersection types in the context, union types in the full type)
mtString :: (Monad m, B t) => m (t String)
mtString = return mtInt -- use a bound that is implied by another bound (implicit upcasting)

optString :: Maybe String
optString = join mtString -- full types are contravariant in their contexts

Параметричний поліморфізм вищого роду

-- parametrize types over type variables that are themselves parametrized
data OneOrTwoTs t x = OneVariableT (t x) | TwoFixedTs (t Int) (t String)

-- bounds can be higher-kinded, too
class MonadStrip s where
  -- use arbitrarily nested higher-kinded type variables
  strip :: (Monad m, MonadTrans t) => s t m a -> t m a -> m a

Основне введення тексту

Це важко дати прямий приклад, але це означає, що кожен вираз має рівно один максимально загальний тип (його називають основним типом ), який вважається канонічним типом цього виразу. З точки зору поліморфізму підтипу на основі обмежень (див. Вище), основним типом виразу є унікальний підтип кожного можливого типу, до якого може бути використаний цей вираз. Наявність головного введення в (нерозширений) Haskell - це те, що дозволяє зробити повний висновок типу (тобто, вдалий умовивід для кожного виразу, без необхідних анотацій типу). Розширення, які порушують основне введення тексту (яких існує багато), також порушують повноту виводу типу.


7
Не використовуйте lв якості змінної однієї літери, її ДУЖЕ важко відрізнити 1!
recursion.ninja

5
Можливо, варто відзначити, що, хоча ви абсолютно праві, що Java має певну інформацію про тип виконання, а Haskell цього не робить, ви можете використовувати клас типу Typeable в Haskell, щоб надати щось, що веде себе як інформація про тип виконання для багатьох типів (з новішими PolyKinded класів в дорозі, це будуть всі типи iirc), хоча, я думаю, це залежить від ситуації, передає вона фактично інформацію про тип під час виконання чи ні.

3
@DarkOtter Я знаю Typeable, але у Haskell 2010 цього немає (можливо, Haskell 2014 буде?).
Полум’я Птарієна

5
А як щодо (закритих) видів сум? Вони є одним із важливіших механізмів кодування обмежень.
tibbe

3
Нікчемність? Здоровість (відсутність коваріаційних синусів)? Закриті суми / відповідність шаблону? Ця відповідь занадто вузька
Пікер

32

Системі типу Java не вистачає поліморфізму вищого роду; Система типу Haskell має його.

Іншими словами: у Java конструктори типів можуть абстрагуватися над типами, але не над конструкторами типів, тоді як у Haskell конструктори типів можуть абстрагуватися над конструкторами типів, а також типами.

Англійською: в Java загальний не може приймати інший загальний тип і параметризувати його,

public void <Foo> nonsense(Foo<Integer> i, Foo<String> j)

а в Haskell це досить просто

higherKinded :: Functor f => f Int -> f String
higherKinded = fmap show

28
Розумієте, що це ми знову, англійською мовою? : P
Мейсон Уілер

8
@Matt: Як приклад, я не можу написати це на Java, але можу написати еквівалент у Haskell : <T<_> extends Collection> T<Integer> convertStringsToInts(T<string> strings). Ідея тут полягала б у тому, що якби хтось назвав його так, як convertStringsToInts<ArrayList>він би взяв масив рядків і повернув масив цілих чисел. І якщо вони замість цього використовувались convertStringsToInts<LinkedList>, це було б те саме, що і зв'язаними списками.
sepp2k

8
Це не поліморфізм вищого роду, а не ранг 1 проти п?
Адам

8
@ JörgWMittag: Я розумію, що поліморфізм вищого рангу стосується того, де можна поставити forallсвої типи. У Haskell тип a -> bнеявно forall a. forall b. a -> b. За допомогою розширення ви можете зробити ці foralls явними і перемістити їх.
Тихон Єлвіс

8
@Adam є rigtht: вищий ранг і вищий рід абсолютно різні. GHC також може виконувати типи вищого рангу (тобто типи forall) з деяким розширенням мови. Java не знає ні вищих, ні вищих категорій.
Інго

11

Щоб доповнити інші відповіді, у системі типів Haskell немає підтипу , в той час як вводиться об'єктно-орієнтована мова як Java.

В теорії мов програмування , підтипи (також підтип поліморфізму або включення поліморфізму ) є формою типу поліморфізму , в яких підтип є типом даних , який пов'язаний з іншим типом даних ( супертіп ) деяким поняттям взаємозамінності , а це означає , що програмні елементи, як правило , підпрограми або функції, написані для роботи над елементами супертипу, можуть також працювати над елементами підтипу. Якщо S є підтипом T, відношення підтипу часто пишеться S <: T, щоб означати, що будь-який термін типу S може бути безпечно використаний у контексті, колиочікується термін типу T. Точна семантика субтипування вирішально залежить від деталей того, що "безпечно використовується в контексті, де" означає дану мову програмування. Система типів мови програмування, по суті, визначає своє власне відношення підтипів, яке цілком може бути тривіальним.

Через відношення підтипу термін може належати до більш ніж одного типу. Отже, субтипізація є формою поліморфізму типу. У об'єктно-орієнтованому програмуванні термін «поліморфізм» зазвичай використовується для позначення виключно цього політипу підтипу , тоді як методи параметричного поліморфізму вважатимуться загальним програмуванням ...


4
Хоча це не означає, що ви не отримуєте спеціального поліморфізму. Ви просто в іншій формі (введіть класи замість підтипу поліморфізму).

3
Підкласифікація не є підтипом!
Френк Ширар

8

Досі ніхто не згадував - це висновок про тип: компілятор Haskell зазвичай може зробити висновок про тип виразів, але вам доведеться детально розповісти компілятору Java про свої типи. Суворо, це особливість компілятора, але дизайн системи мови та типів визначає, чи можна зробити висновок про тип. Зокрема, висновок типу погано взаємодіє з поліморфізмом підтипу Java та спеціальним перевантаженням. На відміну від цього, дизайнери Haskell намагаються не вводити функції, що впливають на умови вибору.

Інша річ, яку, здається, люди досі не згадували - це алгебраїчні типи даних. Тобто здатність будувати типи із сум ('чи') та продуктів ('і') інших типів. Класи Java роблять продукти (поле a і поле b, скажімо) чудовими. Але вони насправді не роблять сум (скажімо, АБО поле B, скажімо). Scala має кодувати це як декілька класів випадків, що не зовсім однаково. І хоча це працює для Scala, трохи сказати, що Java має це.

Haskell також може побудувати типи функцій за допомогою конструктора функцій, ->. Хоча методи Java мають підписи типів, їх не можна комбінувати.

Система типу Java дозволяє включити тип модульності, якого Haskell не має. Мине час, перш ніж з'явиться OSGi для Haskell.


1
@MattFenwick, я змінив 3-й параграф на основі ваших відгуків. Системи двох типів розглядають функції по-різному.
GarethR

Я б не називав ADTs особливістю системи типів. Ви можете повністю (якщо незграбно) імітувати їх OO обгортками.
Лише близько

@leftaroundabout Я думаю, що це можливо. Наприклад, можуть бути речі, які є вродженими для типової системи однієї мови, але можуть бути реалізовані з моделями дизайну в іншій. Очевидно, що модель дизайну, порівняно з рідною, є вирішенням. Вирішення проблеми через слабку систему.
Привіт-Ангел

Обрана відповідь згадала «повний висновок типу» у розділі «Основне введення тексту». Java може сортувати емуляцію сум з підтипами та інформацією про тип виконання, але, як ви кажете, це не те саме, що сума не є цілісним атрибутом системи типів.
Шелбі Мур III
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.