Чому в Go немає жодних дженериків?


126

Відмова: Зараз я грав із Go тільки один день, тому є хороший шанс, що я багато чого пропустив.

Хтось знає, чому немає реальної підтримки для генеричних / шаблонів / whatsInAName в Go? Отже, є загальне map, але це постачається компілятором, тоді як програміст Go не може написати власну реалізацію. Якщо говорити про те, щоб зробити Go максимально ортогональним, чому я можу ВИКОРИСТОВУВАТИ загальний тип, але не СТВОРИТИ новий?

Особливо, що стосується функціонального програмування, є лямбдаси, навіть закриття, але в системі статичного типу бракує дженериків, як я можу писати, ну, такі загальні функції вищого порядку, як filter(predicate, list)? Гаразд, пов'язані списки тощо можуть бути зроблені з interface{}жертвою безпеки.

Оскільки швидкий пошук в SO / Google не виявив ніяких розумінь, схоже, що дженерики, якщо вони взагалі будуть додані до Go, як думка. Я довіряю Томпсону, щоб зробити це краще, ніж хлопці з Java, але навіщо тримати генерики? Або вони заплановані і просто ще не виконані?


Я думаю, що варто зазначити: використання інтерфейсу {} не приносить шкоди безпеці типу. Це тип, і його можна стверджувати (не відзначати) іншим типам, але ці твердження все ще викликають перевірки виконання, щоб підтримувати безпеку типу.
cthom06

12
interface{}жертвує безпекою статичного типу. Однак це є дещо дивна скарга, коли слід згадати про схему - наступний абзац, оскільки схема зазвичай не має статичної перевірки типу.
poolie

@poolie: Те, що мене хвилює, - це дотримуватися ОДНОЇ парадигми в мові. Або я використовую статичний тип безпеки XOR ні.

2
btw це написано "Ідіть", а не "Ідіть", як ви бачите на golang.org. І це враховує регістри. :-)
poolie

Відповіді:


78

цю відповідь ви знайдете тут: http://golang.org/doc/faq#generics

Чому Go не має загальних типів?

У певний момент цілком може бути додана дженерика. Ми не відчуваємо для них нагальності, хоча ми розуміємо, що деякі програмісти роблять.

Генеріки зручні, але вони коштують за складністю в системі типів та часу роботи. Ми ще не знайшли конструкцію, яка надає значення, пропорційне складності, хоча ми продовжуємо над цим думати. Тим часом, вбудовані карти та фрагменти Go, а також можливість використовувати порожній інтерфейс для побудови контейнерів (з явним розпакуванням) означають, що в багатьох випадках можна писати код, який робить те, що дозволить генерика, якщо менш плавно.

Це залишається відкритим питанням.


14
@amoebe, "порожній інтерфейс", написаний interface{}, є самим основним типом інтерфейсу, і кожен об'єкт надає його. Якщо ви зробите контейнер, у якому є вміст, він може прийняти будь-який (не примітивний) об'єкт. Тож це дуже схоже на контейнер, що вміщує Objectsв Java.
poolie

4
@YinWang Generics не настільки простий у певному середовищі. Що ще більш важливо; інтерфейс {} не еквівалентний покажчикам недійсності * у C. Кращими аналогіями будуть типи ідентифікаторів System # Object або C # на C #. Інформація про тип зберігається і може бути "повернена" (стверджується, фактично) назад до її конкретного типу. Докладні подробиці можна отримати тут: golang.org/ref/spec#Type_assertions
tbone

2
Система @tbone C # 's System.Object (або Об'єкт Java як такої) - це, по суті, те, що я мав на увазі під «курсорами недійсності C» (ігноруючи частину, на якій не можна робити арифметику вказівника на цих мовах). Саме там інформація статичного типу втрачається. Акторський склад не дуже допоможе, оскільки ви отримаєте помилку виконання.
Ян

1
У шаблонів @ChristopherPfohl D, здається, є трохи менше накладних витрат на компіляцію, і, як правило, ви не генеруєте більше коду з шаблонами, ніж зазвичай, як це робиться в іншому випадку (ви можете, фактично, мати менше коду залежно від обставин).
Кубік

3
@ChristopherPfohl Я думаю, що лише у дженериків Java є питання про бокс / розблокування для примітивних типів? C # 's reified generic не має проблеми.
ca9163d9

32

Ідіть 2

На веб- сайті https://blog.golang.org/go2draft є проект дизайну для дженериків .

Ідіть 1

Расс Кокс, один з ветеранів Go, написав допис у блозі під назвою «Загальна дилема» , в якій він запитує

... чи хочете ви повільних програмістів, повільних компіляторів і роздутих бінарних файлів або повільних термінів виконання?

Повільні програмісти є результатом відсутності дженериків, повільні компілятори викликаються C ++, як генерики, а повільний час виконання випливає з підходу бокс-unboxing, який використовує Java.

Четверта можливість, про яку не згадується в блозі, йде маршрутом C #. Генерування спеціалізованого коду, як у C ++, але під час виконання, коли це потрібно. Мені це дуже подобається, але Go дуже не схожий на C #, тому, мабуть, це взагалі не застосовується ...

Слід зазначити , що з допомогою популярних Java 1.4 , як методика узагальненого програмування в ходу , що закиди вinterface{} ході, страждає від таких самих проблем, як і бокс-розпакування (адже це те, що ми робимо), крім втрати безпеки часу компіляції. Для невеликих типів (наприклад, ints) Go оптимізує interface{}тип так, що список вкладених інтерфейсів {} займає суміжну область пам'яті і займає лише вдвічі більше місця, ніж звичайні ints. Однак все ще є накладні перевірки часу виконання, поки ви робите кастинг interface{}. Довідково .

Усі проекти, які додають загальну підтримку (їх декілька, і всі вони цікаві) рівномірно йдуть маршрутом C ++ генерації часового коду.


Моє рішення цієї дилеми полягало б у тому, щоб перейти за замовчуванням до "повільного часу виконання" з можливістю профілювати програму та перекомпілювати найбільш чутливі до продуктивності частини в режимі "повільні компілятори та роздуті бінарні файли". Шкода, що люди реально реалізують подібні речі, як правило, проходять маршрут С ++.
user7610

1
Згадувалося, що малі типи (тобто int), які зберігаються при []interface{}використанні в 2x в якості ОЗУ []int. Хоча правда, навіть менші типи (тобто байт) використовують до 16 разів оперативної пам'яті як []byte.
BMiner

Діалеми з підходом C ++ насправді немає. Якщо програміст вирішив написати шаблон шаблону, перевага при цьому повинна перевищити вартість повільної компіляції. В іншому випадку він міг би зробити це по-старому.
Джон З. Лі

Дилема полягає в тому, який підхід обрати. Якщо ви вирішите дилему, використовуючи підхід C ++, дилема вирішується.
користувач7610

9

Незважаючи на те, що в даний час generics не вбудований, є кілька зовнішніх реалізацій generics для go, які використовують коментарі в комбінаціях з невеликими утилітами, що генерують код.

Ось одна така реалізація: http://clipperhouse.github.io/gen/


1

Власне, відповідно до цього допису:

Багато людей зробили висновок (неправильно), що позиція команди Go: "Іди ніколи не матиме дженериків". Навпаки, ми розуміємо, що потенційні генеричні засоби мають як зробити Go набагато більш гнучким і потужним, так і зробити Go набагато складнішим. Якщо ми хочемо додати дженерики, ми хочемо зробити це таким чином, щоб отримати якомога більше гнучкості та потужності з якомога меншою додатковою складністю.


-1

Параметричний поліморфізм (дженерики) знаходиться на розгляді для Go 2 .

Цей підхід вводить поняття контракту , яке може бути використане для вираження обмежень щодо параметрів типу:

contract Addable(a T) {
  a + a // Could be += also
}

Такий договір може бути використаний таким чином:

func Sum(type T Addable)(l []T) (total T) {
  for _, e := range l {
    total += e
  }
  return total
}

Це пропозиція на цьому етапі.


Ваша filter(predicate, list)функція може бути реалізована з таким параметром типу:

func Filter(type T)(f func(T) bool, l []T) (result []T) {
  for _, e := range l {
    if f(e) {
      result = append(result, e)
    }
  }
  return result
}

У цьому випадку не потрібно обмежуватись T.


1
Якщо ви сьогодні читаєте цю відповідь, майте на увазі, що контракти відхилено від проекту пропозиції: go.googlesource.com/proposed/+/refs/heads/master/design/…
jub0bs
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.