Про идеологические недостатки решения: было бы справедливо отметить что:
Использование исключений в данном случае лежит полностью на совести автора. При желании можно было бы организовать монаду и тогда было бы все "по функциональным канонам". Другое дело не понятно какой от этого выигрыш.
Ленивость вычислений в конкретно этой ситуации зависит не от языка, а от реализации. К текущему коду можно добавить небольшой кусок и выражения станут вычисляться лениво.
Если цель «придерживаться принципов функционального программирования», то все сделано верно. Но учитывая что Kotlin в основном объектно-ориентированный язык, то конечно операции типа `solve` и `toString` лучше делать с помощью наследования и полиморфизма вместо `when`.
Спасибо за статью, приятно почитать аргументированное и конструктивное мнение.
По поводу «проблем» которые не будут чиниться — то что затронуто в этой статье не больше и не меньше чем code-style. То бишь что тут чинить если это by-design заранее обдуманные случаи использования.
`it` в принципе не предназначен для callback-ов, в документации так и написанно — именуйте параметр кроме простейших случаев. Функция `let` действительно редко встречается в моей практике, но она незаменима для цепочек вызовов с функциями принимающими аргумент, например `mylist.let {LinkedList(it)}.filter {it > 0}`. В случае с `with` мне кажется очевидным что `getLatestComment()` не относится к `dbHelper`, но если это не так, то действительно лучше обойтись явными вызовами по старинке.
В общем, я согласен с автором что иногда неприятности встречаются в коде, но не согласен что это вина языка. Надеюсь давно обещанный продвинутый форматер решит подобные проблемы.
Фразой про необходимость (корректных) измерений я хотел сказать что совсем не очевидно что быстрее — программа на C++ или на Java.
Многие распространенные мифы про тормознутость Java строятся на (не совсем корректных) прикидках.
Ну вот давайте и прикинем. Известно что узкое место в работе (нормального) компьютера это доступ в память. Разработчику не жалко потратить десятки а иногда сотни тактов процессора чтобы избежать лишнего доступа в кеши процессора или в оперативку. 90% времени процессор пользовательского компа простаивает в ожидании подгрузки чего-нибудь из памяти.
Когда загружается программа на C, это более-менее полотно ассемблерных инструкций для всех возможных веток выполнения программы. Вроде понятно.
Когда загружается Java программы, она состоит из JVM байт-кода и интерпретатора JIT. А теперь фокус: байткод и интерпретатор зачастую вместе занимают меньше места чем соответствующий ассемблер. Этим экономится драгоценное место в памяти и кешах. Достигается это тем что JVM байт-код это хорошо упакованные высокоуровневые инструкции, которым могут соответствовать десятки команд ассемблера.
Вот так и получается что действия, которые выполняются относительно редко (95% всей логики), логично интерпретировать. Действия которые выполняются часто и влияют на производительность «распаковывают» из байт-кода в машинные команды различной степени оптимальности.
Когда Вы запускаете IDE вроде IntelijIdea, 5 минут в начале она подтормаживает, происходит тот самый «прогрев» — инициализация и распаковка кода. После этого она начинает ускорятся, и через полчаса летает (это мои собственные ощущения). Готов поспорить что если гипотетически скомпилировать ее в машинные команды (реально не получится ибо рефлекшн), то эти 5-20 минут разогрева потратятся просто на загрузку разбухшего кода в память и прогрев кешей.
На данный момент SpaceX планирует повторно запустить один из уже приземленных первых ступеней этой осенью. Если запуск удасться, это и будет доказательство целесообразности, так как они доставят полезную нагрузку без постройки еще одной ракеты. Будет интересно посмотреть затраты на починку и тестирование, но они должны быть сильно меньше чем $30M. Еще одна статья расходов для повторного запуска это увеличение страховых выплат для груза, но это решается многократными успешными полетами.
Про магию JVM лучше всего слушать Шипилева, рекомендую например вот это: https://youtu.be/nHiEKXpG_4M
Примерно в середине доклада вроде рассказывает про спекуляции с виртуальными методами тоже (тема статьи)
Тестов таких не очень много, насколько я понимаю. Сложно найти программы/проекты на Java/C++ которые бы делали одно и тоже. Не так давно я где то видел сравнение tcp/ip библиотек, там библиотеки на Java были в топе, но это не совсем то.
Вы рассматриваете языки сами по себе, без всего что их окружает. Такой подход совершенно не описывает реальную полезность технологий. Java как язык могла бы спокойно компилироваться в машинный код и это действительно был бы урезанный C++. Я (и некоторые другие) в восторге не от языка, а от JVM-the-platform, и мои комментарии в основном относятся к платформе, а к языку.
Вы хотите сказать что в статье предлагается изучить 8 (!!) языков и все это на стадии «новичок»?
Повторяюсь, я говорил про линковку библиотек, а не управление памятью. Как много людей по Вашему знают что происходит при динамическом подключении .o? Или еще лучше .dll на Windows? Ответ — единицы, остальные «воспринимаю это как данность» (и отлично живут и работают).
Или то-же управление памятью. Кто из C/C++ программистов представляют себе как аллоцируется память? Еще лучше, как она деаллоцируется? А ведь это одна из самых важных характеристик, влияющих на быстродействие программы. Опять же — единицы.
Ну или венец творения — что происходит при ошибках и крашах? В частности при обращении по NULL? Что, «система гарантирует корректность»? Нет, не везде и не всегда. Разные ОС делают это по-разному. И все это необходимо досконально знать чтобы написать минимально-надежную программу.
Простите за прямоту, но мне кажется Вы оперируете какими-то стереотипами что C/C++ сложнее или проще чем Java. В C полно магии на уровне операционки и/или процессора которые нужно хорошо понимать чтобы написать просто корректную программу, я уж не говорю быструю. И наоборот, хорошие Java программисты великолепно разбираются в процессорах, операционках, компиляторах и много в чем другом, что позволяет им выжимать максимум из JVM (те самые случаи когда Java программа быстрее C++ аналога 0_о )
Если под «гимнастикой ума» понимать то что предлагает например Ruby, то Java конечно не в той категории. Несмотря на ограничения синтаксиса, гибкость Java не подлежит сомнению именно из-за количества паттернов и фраймворков которые ее поддерживают. С академической точки зрения Java (JVM) интересна как первый язык решивший проблему линковки библиотек. Вся та мощь и гибкость которую предоставляют полноценная рефлексия, пост-обработка бутекода, аннотации и класслоадеры никогда не снилась C++ и подобным.
Невероятно, но Java вообще изучать не предлагается. Java является промышленным языком #1 последние 10 лет по количеству работы, проектов, и инфраструктуры. Кроме того, Java весьма интересна как язык своим дизайном, например Python и другие языки открыто заимствовали идеи библиотек и фраймворков. С академической точки зрения, JVM это предмет для обязательного изучения любого программиста-теоретика и/или перформансника. Одна JMM чего стоит!
Замечу что если под «не сахарными» расширениями понимать например то, что есть в Swift, где расширение не сильно отличается от метода по свойствам и видно глобально, то получается забавная картина. Swift не имеет аналога Kotlin DSL ровно из-за глобальности их функций-расширений, то есть «хардкорные» фичи проигрывают «сахару» по функциональности. Постройка древовидных DSL очень важная фича Kotlin, и уж точно не является сахаром, так как обеспечивает статические гарантии структуры.
Я так понимаю речь идет о not-null ассертах. Кто-то замерял на сколько замедляется выполнение программы с ними? JIT вполне способен убрать лишние проверки из исполнения, если например они дублируют друг друга. К тому же если я правильно понимаю как JVM оптимизации работают, код с проверками может работать быстрее так как JVM не нужно эмулировать NPE в случае нулевых ссылок.
Под «синтаксическим сахаром» обычно понимается конструкции которые дублируют что-то еще, существующее в языки и/или абстракции, которые «протекают». Функции-расширения не могут быть заменены обычными функциями (в отличие от C#) и не являются методами из-за отличающихся правил диспатча (в отличие от Swift). Это отдельная логическая сущность в Kotlin которая не может быть выражена через остальные примитивы языка.
Расширения не дают доступ к приватным членам класса из вне, пожалуйста проверь это утверждение на коде
«Сахарные примеси аспектного подхода в (Kotlin)» — такого ругательства я еще не слышал. Ни к сахару, ни к аспектам функции-расширения Kotlin особого отношения не имеют, IMO.
Спецификатор `inline` в Котлин гораздо сложнее чем например в C++. Он меняет семантику функции, например влияет на вывод типов, поведение generic параметров или на вид публичного Java-api. Прирост быстродействия в случае с лямбдами это только один из эффектов.
Про идеологические недостатки решения: было бы справедливо отметить что:
Использование исключений в данном случае лежит полностью на совести автора. При желании можно было бы организовать монаду и тогда было бы все "по функциональным канонам". Другое дело не понятно какой от этого выигрыш.
По поводу «проблем» которые не будут чиниться — то что затронуто в этой статье не больше и не меньше чем code-style. То бишь что тут чинить если это by-design заранее обдуманные случаи использования.
`it` в принципе не предназначен для callback-ов, в документации так и написанно — именуйте параметр кроме простейших случаев. Функция `let` действительно редко встречается в моей практике, но она незаменима для цепочек вызовов с функциями принимающими аргумент, например `mylist.let {LinkedList(it)}.filter {it > 0}`. В случае с `with` мне кажется очевидным что `getLatestComment()` не относится к `dbHelper`, но если это не так, то действительно лучше обойтись явными вызовами по старинке.
В общем, я согласен с автором что иногда неприятности встречаются в коде, но не согласен что это вина языка. Надеюсь давно обещанный продвинутый форматер решит подобные проблемы.
Многие распространенные мифы про тормознутость Java строятся на (не совсем корректных) прикидках.
Ну вот давайте и прикинем. Известно что узкое место в работе (нормального) компьютера это доступ в память. Разработчику не жалко потратить десятки а иногда сотни тактов процессора чтобы избежать лишнего доступа в кеши процессора или в оперативку. 90% времени процессор пользовательского компа простаивает в ожидании подгрузки чего-нибудь из памяти.
Когда загружается программа на C, это более-менее полотно ассемблерных инструкций для всех возможных веток выполнения программы. Вроде понятно.
Когда загружается Java программы, она состоит из JVM байт-кода и интерпретатора JIT. А теперь фокус: байткод и интерпретатор зачастую вместе занимают меньше места чем соответствующий ассемблер. Этим экономится драгоценное место в памяти и кешах. Достигается это тем что JVM байт-код это хорошо упакованные высокоуровневые инструкции, которым могут соответствовать десятки команд ассемблера.
Вот так и получается что действия, которые выполняются относительно редко (95% всей логики), логично интерпретировать. Действия которые выполняются часто и влияют на производительность «распаковывают» из байт-кода в машинные команды различной степени оптимальности.
Когда Вы запускаете IDE вроде IntelijIdea, 5 минут в начале она подтормаживает, происходит тот самый «прогрев» — инициализация и распаковка кода. После этого она начинает ускорятся, и через полчаса летает (это мои собственные ощущения). Готов поспорить что если гипотетически скомпилировать ее в машинные команды (реально не получится ибо рефлекшн), то эти 5-20 минут разогрева потратятся просто на загрузку разбухшего кода в память и прогрев кешей.
Примерно в середине доклада вроде рассказывает про спекуляции с виртуальными методами тоже (тема статьи)
Вы рассматриваете языки сами по себе, без всего что их окружает. Такой подход совершенно не описывает реальную полезность технологий. Java как язык могла бы спокойно компилироваться в машинный код и это действительно был бы урезанный C++. Я (и некоторые другие) в восторге не от языка, а от JVM-the-platform, и мои комментарии в основном относятся к платформе, а к языку.
Повторяюсь, я говорил про линковку библиотек, а не управление памятью. Как много людей по Вашему знают что происходит при динамическом подключении .o? Или еще лучше .dll на Windows? Ответ — единицы, остальные «воспринимаю это как данность» (и отлично живут и работают).
Или то-же управление памятью. Кто из C/C++ программистов представляют себе как аллоцируется память? Еще лучше, как она деаллоцируется? А ведь это одна из самых важных характеристик, влияющих на быстродействие программы. Опять же — единицы.
Ну или венец творения — что происходит при ошибках и крашах? В частности при обращении по NULL? Что, «система гарантирует корректность»? Нет, не везде и не всегда. Разные ОС делают это по-разному. И все это необходимо досконально знать чтобы написать минимально-надежную программу.
Простите за прямоту, но мне кажется Вы оперируете какими-то стереотипами что C/C++ сложнее или проще чем Java. В C полно магии на уровне операционки и/или процессора которые нужно хорошо понимать чтобы написать просто корректную программу, я уж не говорю быструю. И наоборот, хорошие Java программисты великолепно разбираются в процессорах, операционках, компиляторах и много в чем другом, что позволяет им выжимать максимум из JVM (те самые случаи когда Java программа быстрее C++ аналога 0_о )
Замечу что если под «не сахарными» расширениями понимать например то, что есть в Swift, где расширение не сильно отличается от метода по свойствам и видно глобально, то получается забавная картина. Swift не имеет аналога Kotlin DSL ровно из-за глобальности их функций-расширений, то есть «хардкорные» фичи проигрывают «сахару» по функциональности. Постройка древовидных DSL очень важная фича Kotlin, и уж точно не является сахаром, так как обеспечивает статические гарантии структуры.
Про JIT я неточно выразился. Имелся в виду любой оптимизирующий компилятор который работает через границы модулей.
Расширения не дают доступ к приватным членам класса из вне, пожалуйста проверь это утверждение на коде