Комментарии 20
Пару раз пытался увещевать: «мол, не делайте так, сделайте лучше нормальный интерфес». В ответ лишь непонимание…
А может пытаются таким образом компенсировать отсутствие дженериков?
Если бы []SomeType
удовлетворяло []SomeInterface
(при том что SomeType
удовлетворяет SomeInterface
), то в большинстве случаев не понадобилась бы ни такая компенсация, ни разработка дженериков в самом языке.
Ну это справедливо лишь для boxed типов.
Если бы []SomeType удовлетворяло []SomeInterface (при том что SomeType удовлетворяет SomeInterface),
Э нет, всё не так просто. Вы говорите о ковариантности и контравариантности. Нельзя просто так сделать, чтобы оно отвечало, ведь если пойти по одной тропинке — другая будет закрыта.
А может пытаются таким образом компенсировать отсутствие дженериков?
0. Может быть вы имели ввиду наследование? Это, бывает, что нужно, да.
1. Но чистые generic же нужны для очень небольших кусков алгоритмов (типа универсальная сортировка на все случаи жизни)
2. Это же можно компенсировать в ряде случаев определенными интерфейсами (кстати, посмотрите как это реализовано в сортировке в Go).
3. Но используют везде — даже в тех случаях, когда не нужно.
Просто силь людей, пришедших с языков с динамической типизацией, и не обращающих внимание, что в статической типизации многие вещи делаются иначе.
1. Я считаю что дженерики хорошо подходят именно в библиотеках, к примеру в стандартной библиотеке Java или C++ их довольно много (особенно при реализации базовых интерфейсов Map, List и т.д) и позволяет обходится без interface{}-хаков. А в Go стандартная библиотека изобилует interface{}-конструкциями, далеко ходить не надо — fmt.PrintLn к примеру, или пакет text/template, где во внутренней реализации их тоже много. Я понимаю что это похоже на C-way, где для этого применяют void*, но не думаю что это хорошая практика в современном программировании.
2. Утиная типизация из интерфейсов хороша, но как показывает практика использования interface{} в библиотеках — не всегда применима.
Просто силь людей, пришедших с языков с динамической типизацией
Согласен, но дженерики всё таки появились в статических языках, когда статическая типизация достигла своего потолка возможностей. Вспомнить хотя бы Java в эпоху до версии 5.0, где повсюду был Object-ад с приведениями типов. Да, в прикладном коде дженерики почти не используются, если только не реализуешь свои абстрактные типы данных или алгоритмы, но в библиотеках это must have.
Так вроде наследование есть в Go.
В Go нет наследования, потому что в современном понимании — это не объектный язык. Есть только имплементация интерфейса (утиная типизация), и встраивание типов. Ни одно из них по своей сути наследованием не является.
Однако с дженериками не так просто. Они нужны постоянно, просто у нас есть два встроенных хака в виде массивов и map.
Можно много говорить, вот читайте сами: stackoverflow.com/questions/6948166/javas-interface-and-haskells-type-class-differences-and-similarities
Но вот маленький пример (из ответа на стэке):
it's really hard to make things like add :: t -> t -> t
with an interface, where it is polymorphic on more than one parameter, because there's no way for the interface to specify that the argument type and return type of the method is the same type as the type of the object it is called on
то есть сделать interface который скажет, вот есть функция, должна работать на любом типе (который имлементирует интерфейс) берет два аргумента и возвращает такой же. На Гоу или Джава интерфейсах такое нельзя гарантировать.
Нет. Классы типов — это совсем другая концепция.
- Контракт (класс типов) — отдельно
- Тип — отдельно
- Реализация контракта (экземпляр класса типов) для типа — отдельно
Код:
age, ok := person["age"].(int)
if !ok {
log.Fatal("could not assert value to int")
return
}
person["age"] = age + 1
Выглядит очень странно и корявенько, это то почему мне не нравится go ( хотя в целом язык - норм ) , платой за статическую типизацию является то что когда ты хочешь гибкости ты должен делать странные workarounds ввиде пустых интерфейсов и обратному приведению типов ( из типа пустой интерфейс в нужный тебе тип ), а любом динамическом языке - это немыслимо
Очень по-уродски выглядит реализация интерфейса. Мы объявляем интерфейс, в нем название метода. Затем в классе реализуем просто метод без указания от какого интерфейса этот метод. Это настолько неявно. Смотришь, что у класса появился метод. Ну появился и появился. А то что это реализация интерфейса я не узнаю, для этого я должен, получается, помнить, что есть такой интерфейс и у него есть такой метод. Весьма абсурдно.
Разбираемся с интерфейсами в Go