Для чого потрібні вищі види?


13

Деякі мови дозволяють проводити класи та функції з параметрами типу (наприклад, List<T>де Tможе бути довільний тип). Наприклад, ви можете мати таку функцію, як:

List<S> Function<S, T>(List<T> list)

Однак деякі мови дозволяють розширити цю концепцію на один рівень вище, що дозволяє мати функцію з підписом:

K<S> Function<K<_>, S, T>(K<T> arg)

Де K<_>саме такий тип List<_>має параметр типу. Цей "частковий тип" відомий як конструктор типу.

Моє запитання: навіщо вам ця здатність? Має сенс мати такий тип, List<T>тому що всі List<T>майже однакові, але всі K<_>можуть бути абсолютно різними. Ви можете мати Option<_>та, List<_>які взагалі не мають спільної функціональності.


7
Є кілька хороших відповідей тут: stackoverflow.com/questions/21170493 / ...
itsbruce

3
@itsbruce Зокрема, Functorприклад у відповіді Луїса Касільяса є досить інтуїтивним. Що робити List<T>і Option<T>мають у загальному? Якщо ви дасте мені або одну, і функцію, T -> Sя можу дати вам List<S>або Option<S>. Інша річ, що їх спільного - це те, що ви можете спробувати отримати Tзначення з обох.
Доваль

@Doval: Як би ти зробив колишнього? Наскільки ця зацікавлена ​​в останньому, я думаю, що це можна вирішити, реалізуючи обидва типи IReadableHolder<T>.
supercat

@supercat Я припускаю , що вони повинні були б реалізувати IMappable<K<_>, T>за допомогою методу K<S> Map(Func<T, S> f), реалізації , як IMappable<Option<_>, T>, IMappable<List<_>, T>. Тож вам доведеться обмежуватись, K<T> : IMappable<K<_>, T>щоб отримати від цього будь-яку користь.
GregRos

1
Кастинг не є відповідною аналогією. Що робиться з класами типів (або подібними конструкціями), це те, що ви показуєте, як заданий тип задовольняє вищому типу. Зазвичай це включає визначення нових функцій або показ яких існуючих функцій (або методів, якщо може використовуватися мова OO типу Scala). Після цього всі функції, визначені для роботи з вищим типом, будуть працювати з цим типом. Але це набагато більше, ніж інтерфейс, оскільки визначено більше простого набору функцій / методів підписів. Я думаю, мені доведеться піти і написати відповідь, щоб показати, як це працює.
йогобрюс

Відповіді:


5

Оскільки ніхто більше не відповів на це питання, я думаю, я сам підкажу. Мені доведеться трохи по-філософськи.

Узагальнене програмування - це абстрагування подібних типів без втрати інформації про тип (що відбувається з поліморфізмом об'єктно-орієнтованого значення). Для цього типи обов'язково повинні мати спільний інтерфейс (набір операцій, а не термін ОО), який ви можете використовувати.

В об'єктно-орієнтованих мовах типи задовольняють інтерфейс завдяки класам. У кожного класу є власний інтерфейс, визначений як частина його типу. Оскільки всі класи List<T>мають один і той же інтерфейс, ви можете писати код, який працює незалежно від обраного Tвами. Ще один спосіб нав'язування інтерфейсу - це обмеження спадкування, і хоча ці два здаються різними, вони наче подібні, якщо задуматися.

У більшості об'єктно-орієнтованих мов List<>не є власне типом. Він не має методів і, отже, не має інтерфейсу. Тільки List<T>такі речі мають. По суті, в більш технічному плані єдині типи, на які ви можете змістовно абстрагуватися, - це ті, хто має такий вид *. Щоб використовувати типи вищого роду в об'єктно-орієнтованому світі, вам потрібно формулювати обмеження типу таким чином, що відповідає цьому обмеженню.

Наприклад, як згадується в коментарях, ми можемо розглядати Option<>і List<>як "відображувану", в тому сенсі, що якщо у вас є функція, ви могли б перетворити " Option<T>в" Option<S>або " List<T>в" List<S>. Пам'ятаючи, що класи не можна використовувати для абстрактного вищого типу безпосередньо, ми замість цього робимо інтерфейс:

IMappable<K<_>, T> where K<T> : IMappable<K<_>, T>

А потім ми реалізуємо інтерфейс List<T>і Option<T>в IMappable<List<_>, T>і як і IMappable<Option<_>, T>відповідно. Що ми зробили, це використання типів вищого роду для розміщення обмежень на фактичні (не вищі) типи Option<T>та List<T>. Так робиться в Scala, хоча, звичайно, Scala має такі функції, як ознаки, змінні типу та неявні параметри, які роблять її більш виразною.

В інших мовах можна абстрагуватися над вищими типами безпосередньо. У Haskell, одному з найвищих авторитетів систем типів, ми можемо викласти клас типу для будь-якого типу, навіть якщо він має вищий тип. Наприклад,

class Mappable mp where
    map :: mp a -> mp b

Це обмеження, розміщене безпосередньо на (неуточненому) типі, mpякий приймає параметр одного типу, і вимагає, щоб воно було пов'язане з функцією, mapяка перетворюється mp<a>на "an" mp<b>. Тоді ми можемо записати функції, які обмежують типи вищого роду так Mappableсамо, як і в об'єктно-орієнтованих мовах, ви могли б встановити обмеження успадкування. Ну, начебто.

Підсумовуючи речі, ваша здатність використовувати типи вищого роду залежить від вашої здатності обмежувати їх або використовувати їх як частину обмежень типу.


Я був занадто зайнятий зміною роботи, але нарешті знайшов час, щоб написати власну відповідь. Я думаю, що вас відволікала думка про обмеження. Одним із важливих аспектів типів вищого роду є те, що вони дозволяють визначати функції / поведінку, які можуть бути застосовані до будь-якого типу, який логічно може бути показаний як кваліфікований. Далі не накладаючи обмежень на існуючі типи, типи класів (одне застосування типів вищого роду) додають їм поведінки.
йогобрюс

Я думаю, що це питання семантики. Клас типу визначає клас типів. Коли ви визначаєте таку функцію, як (Mappable mp) => mp a -> mp b, ви поставили обмеження mpбути членом класу типу Mappable. Коли ви оголошуєте тип, такий як Optionекземпляр Mappable, ви додаєте поведінку до цього типу. Я думаю, ви могли б скористатися такою поведінкою локально, не обмежуючи жодного типу, але тоді це не відрізняється від визначення звичайної функції.
GregRos

Крім того, я цілком певні типи класів не мають прямого відношення до вищих типів. Scala має типи вищого роду, але не типи класів, і ви можете обмежити класи типів до типів із типом, *не роблячи їх непридатними. Безумовно, правда, що типи класів є дуже потужними при роботі з вищими типами.
GregRos

Scala має типи класів. Вони реалізуються з імпліцитами. Імпліцити забезпечують еквівалент екземплярів класу типу Haskell. Це більш крихка реалізація, ніж у Haskell, оскільки для цього немає виділеного синтаксису. Але це завжди було однією з ключових цілей Скали, і вони виконують свою роботу.
йогобрюс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.