Почему в Go нет дженериков?


126

Отказ от ответственности: я играл в Go всего один день, так что есть хороший шанс, что я многое пропустил.

Кто-нибудь знает, почему в Go нет реальной поддержки generics / templates / whatsInAName? Итак, есть общий map, но он предоставляется компилятором, а программист на Go не может написать свою собственную реализацию. Учитывая все разговоры о том, чтобы сделать Go как можно более ортогональным, почему я могу ИСПОЛЬЗОВАТЬ общий тип, но не СОЗДАТЬ новый?

Особенно, когда дело доходит до функционального программирования, есть лямбды, даже замыкания, но с системой статических типов, не имеющей обобщений, как мне написать, ну, общие функции более высокого порядка, такие как filter(predicate, list)? Хорошо, связанные списки и тому подобное можно делать, interface{}жертвуя безопасностью типов.

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


Думаю, стоит отметить: использование интерфейса {} не приносит в жертву типобезопасности. Это тип, и его можно утверждать (не приводить) к другим типам, но эти утверждения по-прежнему вызывают проверки во время выполнения для обеспечения безопасности типа.
cthom06,

12
interface{}жертвует безопасностью статического типа. Однако это несколько странная жалоба, когда упоминается Scheme в следующем абзаце, поскольку Scheme обычно не имеет проверки статического типа.
poolie

@poolie: Меня беспокоит, что я придерживаюсь ОДНОЙ парадигмы в языке. Либо я использую статическую безопасность типа XOR нет.

2
кстати, это пишется «Go», а не «GO», как вы можете видеть на golang.org. И это с учетом регистра. :-)
poolie

1
как насчет github.com/facebookgo/generics ?
Thellimist

Ответы:


78

этот ответ вы найдете здесь: http://golang.org/doc/faq#generics

Почему в Go нет универсальных типов?

В какой-то момент вполне могут быть добавлены дженерики. Мы не чувствуем в них необходимости, хотя понимаем, что некоторые программисты это делают.

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

Это остается открытым вопросом.


14
@amoebe, "пустой интерфейс", пишется interface{}как самый основной тип интерфейса, и каждый объект предоставляет его. Если вы сделаете контейнер, содержащий их, он может принять любой (не примитивный) объект. Так что это очень похоже на контейнер для хранения Objectsв Java.
poolie

4
@YinWang Generics не так просты в среде с предполагаемым типом. Важнее; interface {} не эквивалентен указателям void * в C. Лучшими аналогами были бы типы идентификаторов System.Object C # или Objective-C. Информация о типе сохраняется и может быть «приведена» (фактически утверждена) обратно к ее конкретному типу. Подробности см. Здесь: golang.org/ref/spec#Type_assertions
tbone

2
@tbone C # System.Object (или Java Object per se) - это, по сути, то, что я имел в виду под «указателями void в C» (игнорируя ту часть, что вы не можете выполнять арифметические операции с указателями на этих языках). Именно там теряется информация о статическом типе. Приведение мало поможет, потому что вы получите ошибку времени выполнения.
Ян

1
Шаблоны @ChristopherPfohl D, похоже, имеют немного меньше накладных расходов по времени компиляции, и обычно вы не генерируете больше кода с шаблонами, чем обычно, в противном случае (на самом деле вы можете получить меньше кода в зависимости от обстоятельств).
Cubic

3
@ChristopherPfohl Я думаю, что только у дженериков Java есть проблема с упаковкой / распаковкой для примитивных типов? В обобщенном универсальном C # нет проблемы.
ca9163d9

32

Перейти 2

На https://blog.golang.org/go2draft есть эскизный проект для дженериков .

Перейти 1

Расс Кокс, один из ветеранов го, написал сообщение в блоге под названием «Общая дилемма» , в котором он спрашивает:

… Вам нужны медленные программисты, медленные компиляторы и раздутые двоичные файлы или медленное время выполнения?

Медленные программисты являются результатом отсутствия дженериков, медленные компиляторы вызваны С ++, подобными дженерикам, а медленное время выполнения проистекает из подхода упаковки-распаковки, который использует Java.

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

Я должен упомянуть, что использование популярной техники универсального программирования, подобной Java 1.4, в go, которая приводит к interface{}возникновению тех же проблем, что и упаковка-распаковка (потому что это то, что мы делаем), помимо потери безопасности типов во время компиляции. Для небольших типов (например, целых чисел) Go оптимизирует interface{}тип таким образом, чтобы список целых чисел, приведенных к интерфейсу {}, занимал непрерывную область памяти и занимал в два раза больше места, чем обычные целые числа. Тем не менее, существуют накладные расходы на проверки времени выполнения при приведении из interface{}. Ссылка .

Все проекты, которые добавляют универсальную поддержку (их несколько, и все они интересны), единообразно идут по маршруту C ++ генерации кода времени компиляции.


Моим решением этой дилеммы было бы для Go по умолчанию «медленное время выполнения» с возможностью профилировать программу и перекомпилировать наиболее чувствительные к производительности части в режиме «медленных компиляторов и раздутых двоичных файлов». Жаль, что люди, которые на самом деле реализуют подобные вещи, склонны идти по пути C ++.
user7610 05

1
Было упомянуто, что небольшие типы (например, int), которые хранятся, []interface{}используют вдвое больше ОЗУ, чем []int. Хотя это правда, даже меньшие типы (т. Е. Байтовые) используют до 16 раз больше ОЗУ, чем []byte.
BMiner

На самом деле в подходе C ++ нет дилеммы. Если программист решает написать код шаблона, выгода от этого должна превзойти затраты на медленную компиляцию. В противном случае он мог бы просто сделать это по-старому.
Джон З. Ли

Дилемма состоит в том, какой подход выбрать. Если вы разрешите дилемму, применив подход C ++, дилемма будет решена.
user7610

9

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

Вот одна из таких реализаций: 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
Если вы читаете этот ответ сегодня, имейте в
виду,
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.