дженерики (программистское)
May. 22nd, 2010 12:26 am(эта запись может быть интересна разве что программистам)
Прочитал Java Generics FAQ. 500 страниц, между прочим, в PDF-версии. 500 страниц. Когда вы в последний раз читали FAQ на 500 страниц? Ну вот и я тогда же. С трудом верится даже теперь, по прочтении. Правда, там много места занимают бесконечные индексы и оглавления и заголовки, так что реально "мяса" страниц на 350. Но и этого довольно.
Теперь я понимаю, зачем в Джаве класс Enum определяется, как Enum<E extends Enum<E>>, что это в точности значит, и зачем это нужно. Не уверен, что мне нравится, что я это теперь понимаю.
Многие объяснения в этом фэке читаются, как один сплошной WTF. Ты понимаешь, в процессе чтения, почему это сделано так, а не иначе. Почему и тут исключение, и тут, и это надо делать через задницу, а то даже через задницу не сделать. Почему - один пример наугад из сотни - все обычные вещи с параметризованными типами можно делать, а вот создавать массивы из них нельзя. Кроме случая, когда они параметризованы неограниченными вопросиками. Ты понимаешь, почему это так получается, но не оставляет ощущение глубокого WTF на тему того, как мучительно и болезненно эти дженерики врастают в плоть языка. Сказать, что это leaky abstraction - ничего не сказать; эта лодка не протекает, она буквально состоит из воды.
Вот один из прекрасных вопросов из этого FAQ:
По-моему, внесение дженериков в Джаву было огромной ошибкой. Главная польза от них - строгая типизация коллекций - не оправдывает той огромной цены, которую пришлось заплатить: сложностью языка, читабельностью и понимабельностью кода. Учитывая то, что динамическая безопасность у коллекций и так была, необходимость безопасности на уровне компиляции была кем-то, по-видимому, сильно преувеличена. Да, конечно, намного удобнее и проще написать ArrayList<String>, и знать, что попытка всунуть туда что-то другое вообще не скомпилируется. Это удобнее, чем всунуть что-то не то в ArrayList и получить исключение в рантайме, когда это взяли и попытались привести к String. Баги ловятся быстрее. Но насколько быстрее, как часто на практике это оказывалось существенным, и оправдывает ли эта польза введение архитектуры, приводящей к Enum<E extends Enum<E>> или вопросам, подобным процитированному выше?
Одним из главных преимуществ Джавы по сравнению с C++ было именно grokability исходного кода средним программистом: посмотрев на исходник какого-то класса, который он раньше не видел, средний программист на Джаве - не гуру, не хватающий звезды с неба итд. - мог сказать, что делает каждая строка и зачем это нужно. Дженерики это свойство языка с помпой похоронили. Кто были люди, которые приняли решение так поступить, и радовались ли они красивому трюку само-референтных типов? И это только ущерб понятности кода, не говоря о всем этом огромном числе исключений и плохой совместимости с существующими частями языка - массивами, рефлекцией, исключениями, итд. итд. итд.
Можно ли было добиться той же пользы, что дали дженерики, менее разрушительными способами? Пусть не той же, но главной ее части, мне кажется, можно было. Скажем, какая-нибудь аннотация в момент создания коллекции, подсказывающая компилятору, что там должно быть. Просто сказать: в эту коллекцию я хочу класть строки, или такие-то пары, итп. И пусть компилятор проверяет, что сможет, и вставляет эксплицитное приведение к строкам или парам, когда мы из коллекции что-то достаем. И все. Без extends, без wildcards, для всех мест, где это действительно надо, пусть программист продолжает приводить эксплицитно. Конечно, такая аннотация не покрыла бы все случаи, но самые простые и важные, думаю, покрыла бы. Главное ведь то, что у дженериков почти нулевой эффект на рантайм, поэтому необязательно было вносить их глубоко в ткань языка, so to speak; подсказки компилятору достигают схожей цели. Простая (простая!) и удобная подсказка захватила бы, мне кажется, большинство багов с типами в коллекцях, которые до дженериков проявлялись только в рантайме.
(disclaimer: я редко и мало пишу на Джаве, и не эксперт в ней)
Прочитал Java Generics FAQ. 500 страниц, между прочим, в PDF-версии. 500 страниц. Когда вы в последний раз читали FAQ на 500 страниц? Ну вот и я тогда же. С трудом верится даже теперь, по прочтении. Правда, там много места занимают бесконечные индексы и оглавления и заголовки, так что реально "мяса" страниц на 350. Но и этого довольно.
Теперь я понимаю, зачем в Джаве класс Enum определяется, как Enum<E extends Enum<E>>, что это в точности значит, и зачем это нужно. Не уверен, что мне нравится, что я это теперь понимаю.
Многие объяснения в этом фэке читаются, как один сплошной WTF. Ты понимаешь, в процессе чтения, почему это сделано так, а не иначе. Почему и тут исключение, и тут, и это надо делать через задницу, а то даже через задницу не сделать. Почему - один пример наугад из сотни - все обычные вещи с параметризованными типами можно делать, а вот создавать массивы из них нельзя. Кроме случая, когда они параметризованы неограниченными вопросиками. Ты понимаешь, почему это так получается, но не оставляет ощущение глубокого WTF на тему того, как мучительно и болезненно эти дженерики врастают в плоть языка. Сказать, что это leaky abstraction - ничего не сказать; эта лодка не протекает, она буквально состоит из воды.
Вот один из прекрасных вопросов из этого FAQ:
What is the difference between a Collection<Pair<String,Object>>, a Collection<Pair<String,?>> and a Collection<? extends Pair<String,?>>?Ответ начинается так: "All three types refer to collections that hold pairs where the first part is a String and the second part is of an arbitrary type. The differences are subtle." Потом на двух страницах объясняются эти subtle differences.
По-моему, внесение дженериков в Джаву было огромной ошибкой. Главная польза от них - строгая типизация коллекций - не оправдывает той огромной цены, которую пришлось заплатить: сложностью языка, читабельностью и понимабельностью кода. Учитывая то, что динамическая безопасность у коллекций и так была, необходимость безопасности на уровне компиляции была кем-то, по-видимому, сильно преувеличена. Да, конечно, намного удобнее и проще написать ArrayList<String>, и знать, что попытка всунуть туда что-то другое вообще не скомпилируется. Это удобнее, чем всунуть что-то не то в ArrayList и получить исключение в рантайме, когда это взяли и попытались привести к String. Баги ловятся быстрее. Но насколько быстрее, как часто на практике это оказывалось существенным, и оправдывает ли эта польза введение архитектуры, приводящей к Enum<E extends Enum<E>> или вопросам, подобным процитированному выше?
Одним из главных преимуществ Джавы по сравнению с C++ было именно grokability исходного кода средним программистом: посмотрев на исходник какого-то класса, который он раньше не видел, средний программист на Джаве - не гуру, не хватающий звезды с неба итд. - мог сказать, что делает каждая строка и зачем это нужно. Дженерики это свойство языка с помпой похоронили. Кто были люди, которые приняли решение так поступить, и радовались ли они красивому трюку само-референтных типов? И это только ущерб понятности кода, не говоря о всем этом огромном числе исключений и плохой совместимости с существующими частями языка - массивами, рефлекцией, исключениями, итд. итд. итд.
Можно ли было добиться той же пользы, что дали дженерики, менее разрушительными способами? Пусть не той же, но главной ее части, мне кажется, можно было. Скажем, какая-нибудь аннотация в момент создания коллекции, подсказывающая компилятору, что там должно быть. Просто сказать: в эту коллекцию я хочу класть строки, или такие-то пары, итп. И пусть компилятор проверяет, что сможет, и вставляет эксплицитное приведение к строкам или парам, когда мы из коллекции что-то достаем. И все. Без extends, без wildcards, для всех мест, где это действительно надо, пусть программист продолжает приводить эксплицитно. Конечно, такая аннотация не покрыла бы все случаи, но самые простые и важные, думаю, покрыла бы. Главное ведь то, что у дженериков почти нулевой эффект на рантайм, поэтому необязательно было вносить их глубоко в ткань языка, so to speak; подсказки компилятору достигают схожей цели. Простая (простая!) и удобная подсказка захватила бы, мне кажется, большинство багов с типами в коллекцях, которые до дженериков проявлялись только в рантайме.
(disclaimer: я редко и мало пишу на Джаве, и не эксперт в ней)
no subject
Date: 2010-05-22 01:01 pm (UTC)Слабость системы типов базового языка она компенсировать не в состоянии.
no subject
Date: 2010-05-22 01:50 pm (UTC)Принцип «пишем вызов метода и не думаем о том, есть ли у этого объекта такой метод, пока нам это не столь важно», — абсолютно одинаковый.
И в джаве так нельзя.
Причины, по которым С++ вот такой, выходят за тему данного треда, в нём обсуждается сам механизм темплейтов и не более, не передёргивайте.
no subject
Date: 2010-05-22 02:11 pm (UTC)no subject
Date: 2010-05-22 02:25 pm (UTC)Ваша точка зрения понятна, спорить тут не с чём, она, по крайней мере, сама себе не противоречит)
Мне трудно оценить, насколько пригодны для серьёзных вещей жабовские конструкции из жутких вайлдкардов, но не читаемы они иногда совершенно.
В С++, впрочем, весь язык такой =(
no subject
Date: 2010-05-22 02:36 pm (UTC)Нет, спасибо, для самоубийства есть более приятные способы.
> жабовские конструкции из жутких вайлдкардов, но не читаемы они иногда совершенно.
Ну да, совместить уродское дерево наследований с параметрическим полиморфизмом - дело нелёгкое, и не могу сказать, чтобы авторы жабы с этим хорошо справились.
no subject
Date: 2010-05-22 02:44 pm (UTC)Тот, кому удастся создать систему типов, прекрасную как в Haskell и в то же время дружественную объектно-ориентированным мозгам, как в Java, прикрутив к этому все плюсы и того, и другого, может сразу основывать новую религию. Мировую.
Хотя я почти уверен, что при этом вылезут тонны проблем, нам пока не очевидных.
Это сродни ситуации в нынешней физике - есть квантовая механика, есть теория относительности, они противоречат друг другу, сделать ничего нельзя. Но все верят, что придёт тот, кто их объединит и создаст Теорию Всего.
no subject
Date: 2010-05-22 09:05 pm (UTC)no subject
Date: 2010-05-23 06:17 pm (UTC)no subject
Date: 2010-05-25 08:26 am (UTC)Фанатам что-то рассказывать - дело гиблое, на то они и фанаты. Я лично согласен с предыдущим оратором - "утиная типизация" это полный кошмар. Чем менее строгий язык - тем сложнее на нем изобразить что-нибудь путное. И тем больше у него поклонников, кстати. Ибо напрягаться и укладываться в ограничения никто не любит, а оценить их пользу - это надо иметь определенный уровень.
> Мне трудно оценить, насколько пригодны для серьёзных вещей жабовские конструкции из жутких вайлдкардов, но не читаемы они иногда совершенно.
Подобные конструкции, как правило, появляются в библиотечном коде. Там, где нужна наибольшая гибкость и универсальность. Вот там они действительно дают большое преимущество. Однако я лично считаю, что создание библиотек общего назначения - это самое сложное, что есть в разработке. Человек, который этим занимается, должен быть профессионалом. А у них, как правило, "жабовские конструкции из жутких вайлдкардов" проблем не вызывают.
Остальные же разработчики с подобными конструкциями дела практически не имеют. Я на Java 5 разрабатываю с появления первой беты, и мне ни разу не пришлось использовать в прикладном коде конструкцию, например,
? super X. В лучшем случае -? extends X. Но чаще всего это было в библиотечном коде, которые потом в использовании был не сложнее, чем стандартные коллекции Java.no subject
Date: 2010-05-26 07:26 pm (UTC)? super X я тоже ни разу в прикладном коде не использовал, а вот ? extends X приходилось, и не раз. Причем для совершенно тривиальных вещей.
> Человек, который этим занимается, должен быть профессионалом. А у них, как правило, "жабовские конструкции из жутких вайлдкардов" проблем не вызывают.
А что вы понимаете под «проблемами»?))
полное непонимание того, что написано? Ну я, например, всегда разобраться могу. Это делает такую конструкцию более читаемой? Нет.
Будто полумифические «профессионалы» — это люди, которые думают сразу в синтаксических конструкциях и которым удобство и очевидность до лампочки. (то есть я не знаю, может оно и правда так, просвятите)