Pull to refresh

Comments 319

> Говорят, все новые мобильные проекты на Андроиде пишут исключительно на Котлине.
Врут
конечно, что не на Котлин, то на React Native.
React Native — легаси и прошлый век. Flutter
Как быстро во фронтенде летит время.
Один час на Земле равен 30 годам во фронтенде…
Да ладно, флаттер нормальная технология. Самое главное что к js, html и css отношения не имеет.
Олег забыл написать, что на Мобиус будет выступать Святослав Щербина из JetBrains с рассказом о том, как писать мобильные приложения на Kotlin Muplitplatform.
А нафига вам тернарный оператор, если if — это expression?

потому что if требует скобок, больше текста, имхо он менее четаем

Если не надо вычислять первое значение, то можно коротко так:


var t = Value0.takeIf(expression) ?: Value1

костыль, но чем-то похож на тернарный...

Это одна из причин, по которым автору статьи и не нравится Котлин — чрезмерное использование «идеоматических» конструкций. В данной ситуации if будет более разумным выбором, на мой взгляд.
Я бы даже сказал по-другому: «чрезмерное использование конструкций „костыль, но чем-то похож на...“)))
тернарный оператор в Kotlin записывается вот так

val max = if (a > b) a else b


это возможно потому, что блоки внутри if возвращают значение
UFO just landed and posted this here
Тут можно много о чем спорить, но большинство ваших негодований выглядят притянутыми за уши либо звучат как «смотрите какой быдлокод я могу написать, а язык не мешает».
Проблема в том, что в этом — сама суть Java: язык, в котором на разработчика надевают смирительную рубашку, так что даже человек без опыта, изучивший Java на двухнедельных курсах может что-то писать по данному ему техзаданию.

Kotlin в эту нишу не вписывается от слова «совсем»… с чем разработчикам, долго работавшим на Java, тяжело смириться.

P.S. Собственно если вы посмотрите на историю развития Java — то это постоянная борьба между людьми, которые хотят новые фичи, так как это позволяет писать более читабельный код — и людьми, которые пытаются их не допустить, потому что они также позволяют читать менее читабельный код…
UFO just landed and posted this here
> в джаве уже вроде как признали, что они были ошибкой

Кто эти люди, которые признали? Рейнхольд? Роуз? Гёц? Мне checked exceptions как раз кажутся очень крутой фичей, всегда их юзал.
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here

Проблема checked exceptions в том, что в конструкции throws нельзя использовать типы-параметры.


Как следствие, если тип Either ErrorsADT еще можно выразить на Java, то тип Either в рамках вашего изоморфизма уже невыразим.


Собственно, на этом все ФП и заканчивается.

UFO just landed and posted this here
UFO just landed and posted this here
Так всё упирается в то, что давно и очень быстро делали. Дженериков-то там, на самом деле, нету.

Всё что есть — сделано через динамическую типизацию и обмазку в компайл-тайм.
через динамическую типизацию

Не совсем. Просто компилятор при получении из метода T get() проставляет приведения Integer i = (Integer)get(); к заранее известным типам. Дальше доступ к полям и методам идёт как обычно. Поиска по имени метода при каждом вызове, к счастью, нет.

Проблема в том, что рантайме никакого T get() нету. Есть вовсе даже Object get(). И он, вообще говоря, может вернуть что угодно. Динамическая типизация в чистом виде.

А приведение типов и выброс исключения — это как раз та «обвязка», о которой я говорил.

Это неплохо работает, на самом деле, TypeScript устроен почти так же. Просто нужно не забывать об ограниченности такого подхода.

Статическая и динамическая типизация — это фичи языка, а не рантайма. И вернуть что угодно он не может: компилятор гарантирует*, что этот каст всегда будет успешным (при соблюдении некоторых правил).


* на самом деле нет. Чертовы массивы все портят. Вот за каким надом их решили сделать ковариантными?

Не гарантирует компилятор нифига. Раздельная компиляция потому что. И ничто вам не мешает изменить код так, чтобы все ваши инварианты порушились.
Ну, если компилировать с одной версией библиотеки, а использовать потом другую — то да, так и будет. Но зачем так делать-то?
Там есть соседняя тема, где всё это уже обсудили в очередной раз, с самыми последними аргументациями.

То, что язык позволяет загружать динамические библиотеки, не делает его языком с динамической типизацией. Это динамическая компоновка.
По аналогии, даже в С/С++ можно подсунуть неправильную dll (но в нормальных сборках всё-таки используют корректные dll, а не какие попало), но язык динамическим от этого не станет. Подозреваю, что даже в хаскеле так можно.

Проблема checked exceptions в том, что в конструкции throws нельзя использовать типы-параметры.

Вполне можно. Вот такой код прекрасно скомпилируется, по крайней мере в Java 8:
public void <E extends Exception> void func() throws E {
}

Хм, почему-то я об этом не знал… И ведь даже вывод типов работает! В таком случае еще не все потеряно.

Нужно только понимать, что это, в действительности, просто отмена checked exceptions.

В рантайме-то там никакого E не будет, будет просто throws Exception (плюс, возможно, автозаворачивание checked exceptions в unchecked).

А какая разница что там в рантайме? Компилятор же гарантирует, что "левым" исключениям просто неоткуда взяться.

Компилятор не может ничего гарантировать про код, которого он не видит.

А .jar, с которым вы собираете ваш проект вовсе не обязан совпадать с тем, что будет реально задеплоено.
UFO just landed and posted this here
> Затем, что это внезапно стандартное поведение для j2ee библиотек.

j2ee — это та технология, от которой в конце концов отказался Oracle, выпилил все её следы из OpenJDK (включая даже такие повсеместные мелочи как java.bind.xml) и отдал на спасение в Eclipse Foundation? =)

отличный пример, чудесный
UFO just landed and posted this here
Ну как никуда не делись, j2ee уже всё — теперь осталась некая надежда на джакарту, и там основной сэллинг поинт это как раз cloud native из коробки, если это выстрелит — то наследие j2ee будет жить

А про спрингбут… Да я видел много всего, но по ходу пришел к выводу, что уберджары на спрингбуте — это самое крутое и удобное :)

И этот подход даже не про джаву, а вообще, про жизнь. Например, я использую GNU/Linux и вижу, насколько круче юзабилити у докерных контейнеров или «все свое тащу с собой» по сравнению с пакетным менеджером. Сейчас у меня есть несколько сайтов про разные вещи, и я везде перешёл к хранению важного софта в self-contained докерах, включая базу данных. Это не просто какой-то непонятно откуда взявшееся утверждение, а моё личное глубокое убеждение о том, как должно выглядеть правильное решение, как я делаю и буду делать у себя.

если я использую какую-то технологию, то жду, что технология поддерживает это убеждение. Если нет, она просто не подходит, надо брать другую.
UFO just landed and posted this here
Если вы собрали всё вместе сами и всё своё тащите с собой (то есть, не используете всякие адовые аппликейшен серверы), то вы полностью контролируете, что за джарки используются. Closed world почти что — всё известно на этапе компиляции. Всё что после этого интересно от среды выполнения — конкретный билд OpenJDK, версия glibc и возможно, ядра Linux. (При этом есть возможность отвязаться и от версии OpenJDK, скомпилировав всё в self-contained exe file, но для этого нужен либо GraalVM, либо Excelsior JET). Очень элегантно. Да, продвигаю это везде — мне нравится, вот и продвигаю. Например, в Golang это поведение по умолчанию.

Компилятор казалось бы гарантирует, но на самом деле для того, чтобы "левые" исключения спокойно появились где угодно достаточно подключить Lombok

Никакого заворачивания точно не будет, потому что в рантайме никаких checked exception нет.
По этой же причине не будет и throws Exception — для JVM просто нет такого понятия, все проверки check exceptions совершает компилятор.
По вашей ссылке нет ничего что бы относилось к ФП. Иммутабельные данные не могут оказаться в «corrupt state» по построению.
Так мы вроде джаву обсуждаем, а не ФП. Про ФП всколь сказано в контексте «А вот в ML», но мы вроде JVM и все к нему относящееся смотрим.

Плюс, эксепшны в любом случае плохо работают с многопотоком. В распространенных языках вставляют всякие известные костыли вроде

To make it easier for developers to write asynchronous code based on Tasks, .NET 4.5 changes the default exception behavior for unobserved exceptions. While unobserved exceptions will still cause the UnobservedTaskException event to be raised (not doing so would be a breaking change), the process will not crash by default. Rather, the exception will end up getting eaten after the event is raised, regardless of whether an event handler observes the exception.

Мы обсуждаем вот это:


А во вторых, они слабо совместимы с функциональным стилем программирования.

Ну вот hidden state кмк с ФП плохо сочетается. ФП поощряет использование типов, и информация об ошибках должна быть в них описана. А получается, что написано, что функция возвращает T, а на самом деле оказывается, что Either<T, Either<Err1,Err2,Err3>>

Рассказывать о том, что на джаве тяжело новичку написать лапшекод — это даже не смешно.
Лапшекод, написанный на Java, те не менее, будет работать и его можно будет понять. А вот в JavaScript можно такого понаписать, что в коде вообще ничего ни понять, ни исправить будет нельзя… Kotlin находится где-то посередине…
UFO just landed and posted this here
Про это я ещё раньше написал. Бегать в смирительной рубашке с завязанными на спине рукавами — тяжело. Но как только вы их отпускаете — «недоучки с двумя месяцами курсов» начинают порождать чёрт-знает-что.

И между этими двумя проблемами развитие Java и прыгает… Kotlin же с самого начала сказал: «да, применяя эти фичи бездумно можно написать непонятный код… и это — не наша проблема», что, собственно, и привело автора статьи в уныние…
UFO just landed and posted this here
А хороший ли разработчик определяются тем, хотят ли они возвращаться с Котлина на Жава или нет?
Проблема в том, что в этом — сама суть Java: язык, в котором на разработчика надевают смирительную рубашку

Да ладно, ничто этому человеку не помешает написать хренову кучу фабрик, адаптеров, визиторов и прочих абстракций на ровном месте. И читать эту писанину потом также тяжело.
Есть ощущение, что это дело привычки. У нас был один пришелец из мира Java — он этих «абстракций» на C++ тоже наплодил столько, что мы их уже третий год изничтожаем.

А ему — нормально, когда один класс без методов расширяет другой класс, тоже без методов, который расширяет ещё один (тоже, разумеется без методов).

Может это как-то связано с Open/Closed Principle из SOLID? На новую функциональность нужно пилить новый класс, старый класс менять нельзя. В Java понятно, что сам факт создания класса есть новая функциональность, ибо описание класса доступно через рефлекшен. Может, там где-то RTTI, или даже полноценный RTTR?

Там было три уровня наследования классов. И у них была куча вспомогательных классов.

Самое весёлое — что всё это было нужно для поддержки разных платформ и вполне можно было бы обойтись буквально парочкой ifdef'ов и некоторым количеством шаблонов.

Но в Java нет ifdef'ов и шаблонов, а для их замены есть… вот это.

В котлине кстати для мультиплатформенной сборки можно использовать expected(объявление без реализации) методы и классы. Некий аналог header файлов из cpp.

Есть ощущение, что это дело привычки.

Есть ощущение, что это от переизбытка умных книжек и бездумного им следования.

Претензия к reduce/fold непонятна и выглядит надуманной. Javascript, Haskell, C# — никто не возвращает Optional! Это же попросту неудобно в большинстве случаев!

UFO just landed and posted this here

Так и я про то же самое говорю. В Котлине все так же, только моноидов нет.

Цепочки вроде ?.let { foo(it); }?.let { bar(it); } — это вообще ад и должны быть запрещены в декларации о правах человека. И это считается идиоматично, Карл. В отличие от нормального if. Читать такой код невозможно.
Знаете, «идеоматично» и «рекомендовано» — это как бы сильно не одно и то же.
На примере JS, посмотрите, какой код компилирует Babel или тот-же Kotlin. Он на 100% идеоматичен, но за написание такого кода руками в приличном обществе принято очень больно бить ногами и эти самые руки отрывать.
Очень странный момент — возможность не указывать возвращаемый тип метода
Во-первых, вам никто не запрещает его указывать, это как минимум правило хорошего тона, даже в языках с динамической типизацией.
Во-вторых, если мне память не изменяет, в Котлине тип dynamic поддерживается только для JS, а для jvm — это вывод типов на этапе компиляции. То есть геморрой с отладкой будет, но скомпилироваться в нерабочий код оно не сможет.
Совершенно очевидно, что среднестатистический быдлокодер забьёт писать свои классы на второй день использования, и код превратится в кошмарную пародию на лисп.
Хорошая предъява к языку.
Это разумная предъява.
Когда-то из языков изгоняли GOTO, чтобы не было ВОЗМОЖНОСТИ писать путанный код.
Язык не только не должен провоцировать писать плохо, он должен провоцировать писать ясно.
Я подозреваю, что Pair существует, чтобы можно было писать `mapOf(key1 to value1, key2 to value2)` и подобное. Инфиксная функция `to` как раз и создает пару.

не обязательно для mapOf:


typealias MyPair= Pair<Int, String>

infix fun Int.to2(that: String): MyPair = MyPair(this, that)

fun main() {
  val m = mapOf(1 to2 "we")
  val l = 2 to2 "test"
}

это удобнее, чем городить data-класс из двух полей и не зависит от конкретной реализации Map.
А ещё это позволяет "городить" именованный перебор key-value коллекций


PS: и да, olegchir забыл упомянуть, что есть не только Pair, но и Triple...

От интеропа с джавой кровь идёт из глаз. А тут всякие JvmStatic и JvmName, и код превращается в цирк с конями.

Согласен, но мое личное мнение — не надо писать на котлине для джавы. Нужно писать на котлине для котлина. Тогда и проблем с интеропом не будет


Давайте ещё навалим про библиотеку. Нафига в стандартную библиотеку языка, который поддерживает дата-классы, включили пары? Это ж прямое поощрение плохого кода.
Совершенно очевидно, что среднестатистический быдлокодер забьёт писать свои классы на второй день использования, и код превратится в кошмарную пародию на лисп

В некоторых ситуациях Pair вполне себе подходит (когда нужно передать пару значений буквально на одной промежуточной операции). Не городить же отдельный бессмысленный дата-класс под каждый такой случай. А что до быдлокодеров — если вы насильно уберет у них Pair это не означает что они сразу станут писать хорошо (или не сделают этот Pair себе сами)


Не стоит забывать, что возможность сделать что-то, не означает необходимость делать это.


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

Непривычно и неудобно по сравнению с Java — только если ничего, кроме Java, в глаза не видеть.

«неудобно по сравнению с Java» вообще больше похоже на оксюморон, как по мне
В Java ты чаще понимаешь по узкому контексту, что происходит. a = b — запись в поле или локал, a[1] = 2 — запись в массив ..

Да, частично это так. Но с помощью такой записи можно существенно упростить код:
Как самый простой пример:
HashMap<String, Map<String, String>> someMap = new HashMap<>();
//Java
someMap.put("key","value");
//Kotlin
someMap["key"] = "value"

Котлин даёт одинаковый API для коллекций и сиквенсов, из-за чего люди злоупотребляют цепочками map/filter на коллекциях, создавая кучу промежуточных неленивых копий

Возможно только по незнанию и на первых порах. Одинаковый api позволяет делать многие сложные преобразования гораздо проще. И чтобы не плодить промежуточные цепочки (хотя иногда и они нужны) достаточно просто перейти к sequence.
Кстати, об IDE. Насколько хороша поддержка Kotlin в IntelliJ IDEA? Она действительно лучше, чем для Java?

Да, она хуже :) но не сильно. Если даже сравнивать с тем же Go, то поддержка языка лучше сделана. (да и сам релиз Kotlin состоялся только в 2016, пара лет всего прошло)
Котлин форсит использование it, что приводит к нечитаемому коду. Что-нибудь типа seq.map { it -> foo(it, 1); }.map { it -> bar(it, 2); }.filter { it -> it.getBaz() > 0; }. Что это вообще было?

Это может вызывать трудности только по началу. It значительно упрощает написание лямбд с одним аргументом. А в тех местах, где есть сложные преобразования всегда можно перейти к именованному аргументу.
Цепочки вроде ?.let { foo(it); }?.let { bar(it); }

Аналогично, сложности только по началу возникают. И Если таких цепочек становится много, то скорее всего что-то делается не так, и скорее всего можно сделать по другому.
От интеропа с джавой кровь идёт из глаз

Отчасти согласен, что местами не очень удобно, но все довольно просто и понятно. Даже тот же @JvmStatic по больше части не нужен, и функция просто выносится на уровень файла.
Экстеншн-методы загрязняют публичный интерфейс такими вещами, о которых автор и подумать боялся.

Автору и не нужно думать :)
Это лишь способ сделать api удобнее у классов, которые чаще всего используются в проекте. И в любом случае от них гораздо больше пользы чем вреда.
Библиотека местами не продумана. Например, reduce.

Тот пример с reduce/fold, что вы привели, это довольно устоявшаяся конструкция. И как уже было сказано выше, текущая реализация вполне удобна.
Давайте ещё навалим про библиотеку. Нафига в стандартную библиотеку языка, который поддерживает дата-классы, включили пары? Это ж прямое поощрение плохого кода.

Как же без пар :) Они весьма полезны. И в коде очень часто возникает необходимость вернуть именно два аргумента, и для этого отлично подходит пара. Да, не везде их нужно использовать, и порой лучше сделать еще один «data class», но для «write once» или просто прототипирования они подходят отлично. А говнокод можно сделать и без них.
Очень странный момент — возможность не указывать возвращаемый тип метода (особенно публичного).

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

Не совсем понятно причем здесь ФП

Прочитал первый абзац и заключение и понял что автор толком ни в чем не разобрался и наверное не особо хочет разбираться.


Без IDE ничего не поймёшь. А IDE плохо, когда ты едешь в поезде и видишь, что свинговый жабоинтерфейс высасывает из ноутбука батарейку как вампир.

Ситуация высосана из пальца. Не соглашусь в корне, в Java тоже есть много всякого во что без IDE сложно въехать. Перегрузку операторов вас никто не заставляет использовать, на Котлине можно писать в Java 6 стиле если очень хочется, дятловать или нет вам решать.

> и наверное не особо хочет разбираться

не просто хочу разбираться, а в некотором смысле это теперь моя работа.

Не совсем понимаю, в чем проблема "Без IDE ничего не поймешь".


Иногда выбор языка основан на степени "ВАУ, СКОЛЬКО ВСЕГО В IDE ДЕЛАЕТСЯ ЗА МЕНЯ!".
Отбрасывать инструмент, с которым проводишь рабочее/личное время потому, что когда-то, возможно, в каком-то поезде, где почему-то нет возможности зарядиться, (но есть необходимость работать) ноутбук сядет на пару часов быстрее — как-то… необычно.

Каждый проект на Github надо открывать в IDE, чтобы прочитать, что там происходит?
Большой и на джаве (особенно на современной, куда вот подвезли стримы и автоматический вывод типов локальных переменных) — да, однозначно. Ну, по крайней мере, в той же мере, в какой и на Kotlin.
UFO just landed and posted this here
UFO just landed and posted this here
Совершенно очевидно, что среднестатистический быдлокодер забьёт писать свои классы на второй день использования, и код превратится в кошмарную пародию на лисп.

Я писал AWS Lambda на Котлине в ФП стиле с карированием, композицией функций и использованием исключительно функций. Так вот если вы вменяемы и пишите тесты до или после ваших функций, то ваш код выглядит не хуже а даже лучше привычного SOLID-ного кода, ибо тут не один класс, одна ответственность, а одна функция.


Не вижу смысла использовать классы как средство защиты от быдлокодеров, это как минимум предвзято. Шедевры такого рода на несколько порядков чаше встречаются, как раз, в мире ООП откуда и родились анти-паттерны типа God Object итд.

UFO just landed and posted this here

Вы невнимательно читали комментарии, правильная цитата должна выглядеть вот так:


ну так никто не заставляет Вас её так использовать.

Вот есть в языке фича — давать переменным произвольные имена. Хорошая она или плохая? Я считаю что хорошая — я могу дать переменным осмысленные имена, и код станет понятнее. Но кто-то другой даст всем переменным имена a1, a2 и a3 — и код будет непонятным. Становится ли фича "давать переменным произвольные имена" от этого неоднозначной?

UFO just landed and posted this here
Ну скажем так… наверное, полезно иметь больше чем одну точку зрения. Чтобы когда условия поменяются, не бегать с горящей задницей кругами :) Точка зрения в посте выбрана очевидно какая, да. Я мог бы написать и с какой-нибудь другой точки зрения, но это неинтересно — обожающих постов, вылизывающих Котлин теплыми щенячьими языками — весь интернет ими переполнен, а толку?

А что не так со стабильностью и долговременной поддержкой? И какое отношение к ней имеют выдуманные претензии из обсуждаемого поста?


Вот три пункта из комментария ниже от Dveim — и те выглядят куда серьезнее...

UFO just landed and posted this here
Вы заходите на второй круг. «Совершенно обоснованные» претензии уже разобрали в других комментариях.

Про долговременные проекты на Kotlin ничего не знаю, поскольку пишу на другом языке. Но половина «ужаснейших» с точки зрения автора поста фич давным-давно есть в C#, на котором написан тот же Stack Overflow. C 2008 года и по наше время — это достаточно долгоживущий проект?
Ну строго говоря, про C# — это не совсем валидный аргумент. Язык — это больше, чем просто набор фич. Недостаточно просто собрать в кучу все что знаешь, и назвать это языком, с таким подходом это будет скорей помойка :) C# — это целостная система ценностей и трейдоффов, другая система.
несколько лет назад сбежал из C# разработки в PHP, тогда еще такого ужаса не было, если сейчас оно там все так, то я рад что сделал это еще тогда.
Сколько лет назад? Большая часть претензий автора актуальна для c# 3.0 который 10 лет назад релизнулся…
Разумеется. В соответствии с вашими описаниями Kotlin — это п$здец, а C++ — это п$здец² (или даже п$здец³). Тем не менее на C++ имеется куча долговременно поддерживаемых проектов. Гораздо, гораздо больше, чем, скажем, на языке Ада — хотя тот, как бы, специально создавался для «стабильности и долговременной поддержки».

Черезмерное увлечение «стабильностью и долговременной поддержкой» приводит к тому, что хорошие разработчики начинают язык избегать (из-за многословности и невозможности писать код кратко и понятно)… и вот это влияет на проекты, в долгосрочной перспективе, куда сильнее, чем возможность писать быдлокод.
UFO just landed and posted this here
1) Он появился давно и альтернатив было немного
Вообще-то тогда (как и сейчас) количество языков исчислялось сотнями. Так что про «отсутствие альтернатив» — не стоит. Вспомните хотя бы Pascal, на котором первые продукты, написанные не на ассемблере, и Apple и Microsoft писали.

2) За 20+ лет появились вот такие вот монстры на основе пота и крови сотни разработчиков
У старых Java проектов — тоже Style Guide'ов хватает, так что мимо.
Мне очень понравились комментарии:

в Котлине есть вот такая вот неоднозначная фича
ну так никто не заставляет Вас её использовать.

Секундочку, Котлин язык прагматичный, построенный, между прочим, на опыте использования Java (зачастую на неоправданных ожиданиях), следовательно язык включает в себя некоторые плюшки и даже дуализм в плане парадигмы. Фичи не лежат мертвым грузом, ими очень активно пользуется сообщество.


Но вот если вас похитили злобные цыгане индо-россияне и заставляют использовать Котлин а вам вообще в ломы, потому что Java это ваше все, то тогда и только тогда, пишите на Котлине в Java 6 стиле, вам ничего не мешает. Иначе зачем вообще переходить на Котлин?

Интересно зачем вводить фичи в язык, которые не нужно/не надо использовать?

Потому что такого никто не говорил. Не нужно передергивать и использовать эти фичи так, как описал автор. Вот с теми же экстеншенами, например, можно на порядок облегчить ряд задач в разработке, улучшить читаемость кода. Нужно просто понимать когда и как эту фичу применять. Написать плохой код можно и без них.

Я пишу на C#, и треть пунктов из поста уже давно и успешно применяются в этом языке. Никто не засоряет код лишними экстеншенами, лишними перегрузками операторов, про ленивое апи коллекций и с чем его кушать могут не знать разве что стажеры/редкие джуны до первого ревью.
UFO just landed and posted this here
go так же плодит новые сущности под каждую задачу, если вы конечно не преобразуете типы через строчку, ну или пустыми интерфейсами грешите много
UFO just landed and posted this here

Любой кейс, архитектурно требующий дженерик. Go предлагает россыпь костылей, плодящих типы.

Не обязательно. Ведь первый совет… Та даааам

Review the requirements
Step back and revisit the requirements. Review the technical or functional specification (you should have one). Do the specs really demand the use of generics? Consider that while other languages may support a design that is based on type systems, Go’s philosophy is different

Этот совет не работает на проектах сложности чуть выше микросервиса. Страдают как раз типы, которые надо или плодить, или вынимать рефлексией и тегами.

Ну, автор статьи это вполне серьезно предлагает, на уровне с «Consider copy-paste».

Статья смешная во многих аспектах. Анти-паттерны приводятся как жизнеспособные стратегии для борьбы со сложностью.

Да я вообще удивлен, откуда у этой истерики положительный рейтинг.

Ничего не могу поделать — видел как пишут обычные люди в условиях жестких дедлайнов, непонятных требований, недостаточной квалификации, необходимости десятилетиями поддерживать непонятную чертовщину итп — и это знание выгорело на сетчатке глаза, сквозь него теперь весь мир воспринимается по-другому.
Я говорил про статью по ссылке, не про вашу :)

По вашим пунктам я постарался дать развернутый ответ ниже.
Каждый раз, когда я вижу эту статью, мне становится очень грустно за Go community. А ведь люди же серьезно в это верят. Как и в «ну ничего, скоро уже добавят generic'и».
Да я глянул слайды с доклада по го2. Такое ощущение, что синтаксис специально сделали таким, чтобы потом тыкать «видите, как неудобно! А мы сразу предупреждали, что это кактус и нинужна».
Да пусть какой синтаксис будет, мне кажется все лучше, чем кодогенерация из командной строки времен Java5.
Не обязательно. Ведь первый совет… Та даааам


Меня особенно порадовал этот пример как аргумент сложности Дженериков:

List<dictionary<string,IEnumerable<HttpRequest>>>

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

Увы, моей фантазии хватает только на два уровня.
Dictionary<string,IEnumerable<HttpRequest>>

— где ключ коллекции — это айдишник соединения, а значение — все реквесты, которые совершило это соединение.

Тем не менее я допущу, что фантазия у меня недостаточно хороша и бизнес-требования под эти три уровня есть. Как итог — мы имеем задачу с бизнес-требованиями в три уровня сложности, которая по своей сути довольно сложна.

К счастью, C# довольно мощный язык, который позволяет относительно легко и понятно типизировать её. На Go — это был бы типичный неподдерживаемый говнокод. Такие «вроде аргументы за, а на самом деле против» только делают языку ещё хуже.

Как если бы АвтоВАЗ заявляли: «заботясь о клиентах мы не ставим в автомобиль кондиционер, ведь он жрет электроэнергию и вместо этого оставляем множество щелей для проветривания».
Из-за минималистичности приходится плодить код. Это очевидный и очень неудобный размен.
UFO just landed and posted this here
Если бы там и правда появлялась всего-то пара лишних условных операторов — я бы с вами согласился.
UFO just landed and posted this here

Если все что нужно с этим foo?.bar?.x сделать — это вернуть, то вы правы. Но в иных случаях там будет еще и две лишних переменных:


var x : X? = null
if (foo != null) {
    val bar = foo.bar
    if (bar != null) {
        x = bar.x
    }
}

Причем проблема тут не только в переменных, но и в отсутствии идиоматического способа написания: этот код можно написать 6 разными способами (два способа с ветками else и 1 без них умножить на переменную bar, которая может быть как внутри, так и снаружи), и это только нормальные способы! А ведь есть еще и вот такие:


var x : X?
if (foo == null) {
    x = null
} else {
    x = null
    val bar = foo.bar
    if (bar != null) {
        x = bar.x
    }
}

На этом фоне достоинство foo?.bar?.x еще и в том, что такое написание единственно, и к нему можно просто привыкнуть, вместо того чтобы раз за разом разбирать очередной порядок написания условных операторов.

А как вам такое: в Kotlin нет checked exceptions. А в JVM-реальности они есть.


Таки в Java-реальности или в JVM?
Ещё немного:
1) котлин хочет усидеть на 3 стульях (js, jvm, native) сразу, и это несовместимо с совместимостью с джавой (put intended). Логика простая: появлятся pure kotlin библиотеки, которые будут частично дублировать функционал уже существующих, но их можно будет использовать в мультиплатформенных проектах. Это приведет к расслоению экосистемы, так как все эти библиотеки будут развиваться независимо друг от друга. Что, в свою очередь, убивает совместимость с джавой; речь не про техническую возможность вызвать код, а про удобство этого процесса.

Пример такого расслоения — scala, где либо есть отдельные java-api, либо библиотеку невозможно использовать из джавы. История повторяется?

Само по себе это разделение не является плохой вещью, но исчезает позиционирование языка как «better java». Что, в случае котлина, является основной selling point для не-андроидоводов.

2) туда же корутины и так называемый kotlin-dsl — библиотека, фундаментально построенная на этом, не будет использоваться из джавы. За примером далеко ходить не надо — ktor пишут лучшие котлинисты мира, но java api там нет.

3) mobile-first развитие => отсутствие плюшек из более новых jvm (8+) в сгенеренном байткоде.
Ничего, по первому пункту есть пример МС с их .Net Standard, которым Jet brains может воспользоваться. Сейчас всё более менее популярные .net библиотеки скомпилированы так, чтоб и можно было использовать на максимальном числе платформ.
Не вижу, Как джава может использовать стандарт. По сути стандарт — это множество C# типов с пустой реализацией, которая компилится в IL. Джава в IL компилиться не будет.

Сделать свой стандарт ничего не мешает, но мне кажется это тоже процесс небыстрый.
Джава компилируется в байткод.

Сделать свой стандарт ничего не мешает, но мне кажется это тоже процесс небыстрый
ну у .Net тоже не сразу получилось. Сначала был PCL.
А разве <kotlin.compiler.jvmTarget>1.8</kotlin.compiler.jvmTarget> не под 8-ку собирает?

Собирает, но без "мелочей" типа invokedynamic (сейчас анонимные классы, по-старинке) и всего остального, что недоступно на текущей андроид jvm.


Возможная аргументация: тогда будет весьма проблематично распостранять библиотеки. Скажем, написал кто-то утилиту под 11 jvm, с ранее несуществующими фичами, залил jar, и при попытке использовать на старой jvm эти самые ранее недоступные фичи будет ошибка. Даже не при попытке, а при подгрузке байткода.


Это решается (можна снова глянуть на пример скалы), но ценой некоторого удобства пользователя. Ну и таких фич немного. Тем не менее, "android-first" развитие, если в 12 jvm выпустят что-то этакое, то котлин очень нескоро будет генерировать соответствующий байткод.

Оказалось, в C++ можно не писать return в методе, который согласно сигнатуре должен что-то возвращать. Это не синтаксическая ошибка согласно стандарту, а undefined behavior. Соответсвенно, программа в рантайме падает с произвольной ошибкой. Чудесный язык — в нем есть специальный синтаксис для неработающих методов.

Я как-то натыкался в коде на такое:
int something;
...
if (blabla...) {
   return x;
} else {
   return something;
}
Извините, за может тупой вопрос. Я пока совсем начинающий. А что в этом коде не так? Или все дело в том, что else можно лишится?
Будет warning при компиляции. И потом, есть же загадочные ..., something может быть инициализирован там
Присоединяюсь к вопросу. Тернарный оператор был бы уместнее, но и так, кажется, всё ок
Возможно я не очень четко это показал, но там возвращается значение неинициализированной переменной. Т.е. в определенной ветке кода функция возвращает тупо «что-то». Причем программист это понимал, что это будет «что-то» и даже переменную назвал соответствующим образом. Функция, иногда возвращающая рандомный мусор вместо ответа — как тебе такое Илон Маск?
Более весело то, что хороший компилятор может эту ветку из кода выпилить. И безусловно возвращать x.
Прошу извинить за оффтом, но недавно просто читал, и напомнило из книги Л.Л.Васильева «Экспериментальные исследования мысленного внушения»:
другой консультант (проф. М. В. Шулейкин), напротив, скептически относился к самой проблеме и подвергал суровой критике все наши опыты. Этим он принес делу большую пользу.
Если в общем — в котлине можно все писать в джава стиле. Да это ущербно, да так делают только новички, но — можно. Так что ты всегда можешь обмазаться if вместо ?.let.

Теперь по пунктам:

it -> foo(it, 1); }.map { it -> bar(it, 2); }.filter { it -> it.getBaz() > 0; }

Ну да, конечно же вместо it, который ни разу не обязателен просто нельзя написать нормальное имя переменной, которое будет понятно читающему такой код, да?


fun List<String>.filterValid(): List<String>
fun List<Int>.filterValid(): List<Int>

Можно было просто написать инлайн класс и на него повесить экстеншн. И да, это и нужно в том числе для того, чтобы решить проблему с кучей экстеншнов в коде. И опять же — ну не нравится — не используй. Всегда можно написать top-level функцию и не мучаться.

Давайте ещё навалим про библиотеку. Нафига в стандартную библиотеку языка, который поддерживает дата-классы, включили пары? Это ж прямое поощрение плохого кода.

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

Но стоит только поменять определения функций на вот такие:

Явно указать нельзя разве? Или мешает чего?

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

Так что ты всегда можешь обмазаться if вместо ?.let

Не всегда, подобные конструкции приходится применять, когда идёт работа с nullable мутабельным полем, т.к. компилятор считает, что они могли измениться после чтения и стать null, не оставляя никакой возможности сказать ему, что разработчик сам заботиться о синхронизации доступа к таким полям. На мой взгляд это самая спорная фича компилятора. Вот тут пытался обсуждать, если интересно подробнее. В итоге обмазываться приходится всеми этими ?.let.
не оставляя никакой возможности сказать ему, что разработчик сам заботиться о синхронизации доступа к таким полям.

О, да ты еще и про котлиновские контракты не слышал! Грустно жить, когда все достижения человечества проходят мимо?
Слышал. А к чему это здесь? Разве это как-то поможет обозначенной проблеме?
Поможет, как раз тем, что можно явно сказать компилятору что в nullable переменной гарантированно будет не null
Если честно, думаю, что повторю предыдущих комментаторов, но тем не менее…
Котлин даёт одинаковый API для коллекций и сиквенсов, из-за чего люди злоупотребляют цепочками map/filter на коллекциях

Проблема программиста же. Если ты не понимаешь, как что-то работает под капотом, или это работает неожиданно, то имеет смысл винить себя и того, кто ревьювил код (если ревью было).
Котлин форсит использование it, что приводит к нечитаемому коду.

Все нормально читается. Неявная подстановка названия итерируемой переменной вполне удобна. Если уж не нравится, то в требования к коду вводить обязательное использование лямбд с именованным(и) параметром(ами).
Цепочки вроде ?.let { foo(it); }?.let { bar(it); }

Их применение зависит от ситуации. Если вам не важно, в каком месте возник null, то вы напишете так, в противном случае шансы увидеть подобный код после адекватного ревью будут достаточно низкими.
Он должен думать обо всех экстеншн-методах, которые любые люди могут добавить в тот же класс?

Вы перевернули все с ног на голову. Я как автор класса используемой библиотеки о таком задумаюсь с очень малой долей вероятности. Если кто-то экстендит мой класс — его право. Однако, изменение данного класса — моя привилегия и если вы хотите продолжать использовать мою библиотеку, то либо меняйте свои расширения, либо используйте старую версию.
Библиотека местами не продумана. Например, reduce

А вот тут я соглашусь с вами. Мне гораздо удобнее вариант, когда аккумулятор ты создаешь сам. К примеру, как это реализовано в Clojure.
Нафига в стандартную библиотеку языка, который поддерживает дата-классы, включили пары? Это ж прямое поощрение плохого кода.

Опять же, если код прошел какое-либо ревью, либо не проходил его (если так принято), то это проблема организации бизнес-процессов компании / квалификации ревьюверов и т.д.
p.s. в данном посте обращение «ты» использовано не в панибратских целях, просто изначально свою мысль сформулировал таким образом.

Если не вдаваться в детали, описанные автором, то можно согласиться с общим месседжем: код на Kotlin-е становится вцелом менее понятный, чем на Java, особенно при наличии определенного знакомства с языком. При первом знакомстве все пишут как на Java, поэтому все достаточно просто. Но узнав немного язык, разработчик начинает теряться в возможностях, код становится более плотным и лаконичным, но менее структурированным. В частности постоянно возникают вопросы:


  • Один класс на файл или несколько классов в файле?
  • Как правильно использовать пакеты?
  • Когда использовать статические определения вне классов, а когда делать их в компаньонах?
  • Использовать экстеншн-методы, или методы класса?
  • Делать ли инициализацию полей при объявлении, или выносить отдельно в init{} — блок? И вообще в Котлине нет четкой границы между определением и поведением. При определении поля можно его и проинициализировать объектом, и сконфигурировать при помощи .apply(), и там же навесить хендлеров и листенеров. Структура класса превращается в кашу.
  • Нагромоздить однострочник или разбить все по действиям?
  • Для операции с объектом переопределить сеттер на свойство или создать отдельный вменяемый метод?
  • Общие рекомендации по стилю.
Автор не понял почему котлин взлетел на андроид — этот язык отвязал программистов от версии jvm, убрал вербозность языка java, встроил самые популярные паттерны программирования в сам язык, добавил возможности dsl которые очень удобно использовать для всевозможных конфигураторов. Хоть котлин и рекламирует 100% совместимость с java но это верно лишь для использования java кода в котлин коде, обратно же никто особо не парится, потому как это дорога в один конец, более высокоразвитый язык поглощает более простой, поэтому не ждите что котлин код будет красиво вызываться из java, совместимость лишь для того чтобы не потерять уже готовый код, но не новый.
Взлетел — относительное понятие… У Котлина есть преимущества, что вы описали, но код реально превращается в непонятную кашу для ревью и для обычного прочтения. А если тем более не будет совместимости с java / android, то дорога для него пойдет в обратную сторну.
Не будет. Как правильно выше сказали — «наличие возможности именовать переменные как угодно, не доставляет проблем».

Многое из того что есть и удивляет в котлине есть в C# и у нас с этим нет трудностей. Напротив — код очень выразителен и краток. Уверен вам тоже самое скажут и Swift — программисты.
этот язык отвязал программистов от версии jvm

Ничего подобного. Внезапно выяснилось, что некоторые конструкции валятся в runtime на 6-ом андроиде, при этом работают на 7 и выше.
Например, мы в команде столкнулись с тем, что map.forEach { (a, b) -> foo() } — матчится в Map.Entry<K, V> (который есть в 6-ой Java), а map.forEach { a, b -> foo() } в BiConsumer<? super K,? super V> (которого нет в 6-ой). При это проект собирается абсолютно без ошибок.
Это не вина котлина, вы использовали java api которое есть в target sdk, но нет в min sdk, а обход ошибки как раз в использовании котлин апи вместо java api
А IDE плохо, когда ты едешь в поезде и видишь, что свинговый жабоинтерфейс высасывает из ноутбука батарейку как вампир.

Белые люди в поездах не кодят, они в поездах отсыпаются или, в худшем случае, кину смотрят.

Котлин не читал, но после этой статьи осуждаю. Однако вынужден вступиться за C++. Да, в сферическо-вакуумном C++ отсутствие return это UB, но любой уважающий себя и пользователя компилятор имеет соотв. диагностику, которую можно легко превратить в ошибку. Ну а не использующие такие компиляторы или отключающие варнинги индивиды получают то, что заслужили.

Имхо такую статью можно про любой язык написать. Так советую прежде чем осуждать "почитать".

Ну шутка же. Парафраз «Пастернака не читал, но осуждаю».
ИМХО как раз пункт про С++ один из немногих валидных.
1. Когда я приезжаю на коне к моему бару, мне легко поставить лошадь у поилки. Машину ставить решительно некуда — непродуманная конструкция автомобиля, нужно было делать авто в форме лошади
2. Я мог бросить лошадь посреди поля, и она сама ела бы траву. Машины нужно заправлять дурацким топливом — что за бред, кто вообще на это пойдет
3. Когда я ездил на коне, я носил шпоры. Теперь шпоры мешают мне нажимать на педали. Их идиотская форма не смогла предусмотреть такую очевидную вещь, которой пользуются все наездники
4. Машина требует дороги! На лошади я могу залезть на любой холм, для машины же требуется специально готовить трассу, заниматься освещением, инфраструктурой и остальными вещами. Раньше я мог остановиться посреди поля и заночевать с конем, а теперь привык пользоваться ортопедическим матрасом и 3-звездной гостиницей.
Разница этого рассказа и спора между Kotlin-Java в том, что Kotlin забирает очень мало, но дает очень много нового и не отказывается от поддержки старого. Впрочем в статье жалоба «мне так непривычно, а значит плохо» — прослеживается слишком явно.
нужно было делать авто в форме лошади

Так ведь сделали. Мотоцикл называется :)
ага, подковы вешать некуда и еще оно навоз не выделяет. Жалкая пародия на оригинал
гвоздь не забьеш! требуется высокотехнологичное производство!
высокотехнологичное производство

как только напильник не обзовут

Раз уж автор попросил в Kotlin Community о конструктивной критике по сути, то она у меня есть.
По пунктам с цитатами из статьи:


Добро пожаловать под кат
В Котлине за любым простым выражением может стоять сколь угодно сложный код из-за всяких умностей вроде перегрузки. Без IDE ничего не поймёшь. А IDE плохо, когда ты едешь в поезде и видишь, что свинговый жабоинтерфейс высасывает из ноутбука батарейку как вампир.

Начать с того, что в чтении/написании Java кода без IDE тоже далеко не уедешь. Что касается самой перегрузки операторов, то в ней особо проблем нет, это всего лишь другой способ объявления методов, который позволяет единообразно писать выражения независимо от типов данных, над которыми эти выражения вычисляются.
Классический пример — BigDecimal в Java:


BigDecimal hundredAndOne = BigDecimal.ONE.add(BigDecimal.TEN.multiply(BigDecimal.TEN));

То же в Kotlin:


val hundredAndOne = BigDecimal.ONE + BigDecimal.TEN ** BigDecimal.TEN

Котлин даёт одинаковый API для коллекций и сиквенсов, из-за чего люди злоупотребляют цепочками map/filter на коллекциях, создавая кучу промежуточных неленивых копий. Стримы в джаве специально введены для различия между ленивой и неленивой коллекцией. Да, есть инспекция в IDE для этого — потому что инспекции призваны исправлять недостатки языков.

В Java, чтобы получить список целых чисел, каждое из которых на 1 больше соответствующего числа из исходного списка нужно будет сделать так:


List<Integer> plusOne = xs.stream().map(x -> x + 1).collect(Collectors.toList());

В Kotlin:


val plusOne = xs.map { it + 1 }

При этом быдлокодеры люди прекрасно в Java делают collect на каждый чих (на прошлом Joker про это был один из докладов) и никакая IDE инспекция им об этом не говорит.


Котлин форсит использование it, что приводит к нечитаемому коду. Что-нибудь типа seq.map { it -> foo(it, 1); }.map { it -> bar(it, 2); }.filter { it -> it.getBaz() > 0; }. Что это вообще было?

Действительно, что это было такое? Если уж использовать it, то не надо стрелочек тогда (рак из точек с запятой в конце каждой строки тоже можно убрать). Надо так:


seq.map { foo(it, 1) }.map { bar(it, 2) }.filter { it.getBaz() > 0 }

Если серьёзно, то в лямбдах (они обычно очень короткие) параметры тоже именуют коротко. Давать длинное имя единственному аргументу, который тут же и используется — много чести. По использованию и так понятно, что в нём, либо же для задачи это не важно.


Цепочки вроде ?.let { foo(it); }?.let { bar(it); } — это вообще ад и должны быть запрещены в декларации о правах человека. И это считается идиоматично, Карл. В отличие от нормального if. Читать такой код невозможно.

Да, с if наверно читаемее:


arg?.let { foo(it) }?.let { bar(it) }

будет равносильно


if (arg != null) {
    val foo = foo(arg)
    if (foo != null) {
        bar(foo)
    } else null
} else null

От интеропа с джавой кровь идёт из глаз. А тут всякие JvmStatic и JvmName, и код превращается в цирк с конями.

Тут соглашусь, использование Kotlin из Java достаточно не удобно просто потому, что очень многих фич Kotlin нет, вот и приходится лишние приседания делать. BTW, обычно Java библиотеки используются из Kotlin, а не в обе стороны. Очень странен проект, где оба языка будут на равных правах присутствовать.


А как вам такое: в Kotlin нет checked exceptions. А в Java-реальности они есть. Отряд специального назначения «Боевые протезы» имеет честь представить новый самоходный костыль @Throws:

Да, в Kotlin выпилили то, что даже в мире Java уже считают антипаттерном (checked exceptions). Для interop сделали аннотацию. В чём проблема?


Автоматические геттеры/сеттеры с добавлением английского слова get и первой буквой проперти в большом регистре (видимо, в локали ENGLISH? Ведь регистр букв системно-зависим) — это страшно.

Чем страшно-то? Геттеры/сеттеры генерируются согласно устоявшимся в Java мире конвенциям.


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

Экстеншн-методы не загрязняют никаких интерфейсов. По факту это просто статические хэлпер методы, которые вызываются через точку.


Библиотека местами не продумана. Например, reduce.

reduce так работает во всех языках, где он есть. Я верно понял, что и в Haskell (foldr1, foldl1), и в ruby, и в python они не продуманы, если верить автору?


Давайте ещё навалим про библиотеку. Нафига в стандартную библиотеку языка, который поддерживает дата-классы, включили пары?

Во-первых для mapOf(...), да и вообще, для тех же целей, для которых в Java есть Map.Entry


Очень странный момент — возможность не указывать возвращаемый тип метода (особенно публичного).

Тип выводится только для методов, которые объявлены как выражение. Если метод абстрактный и тип не указан (в публичном интерфейсе, например), то он Unit по умолчанию. Если нужен не Unit — придётся в интерфейсе явно указать, так что проблема надуманная, как мне кажется. BTW, конвенции по коду в языках с выведением типов (Haskell, Scala, Kotlin и т.д.) требуют указывать их для публичного API


Резюмирую: всё как-то мимо с критикой в статье. Похоже автор плохо разобрался в объекте своей критики.

А я пожалуй соглашусь с последним пунктом, не писать тип функции имеет смысл только на уровне лямбды. Даже в хаскеле емнип не констрейнтить типы функции считается дурным тоном.
Даже если это внутренняя, приватная, функция?

А так-то да, не писать тип проэкспортированных из библиотеки функций — это даже не дурной тон, за такое морду бить надо.
В Хаскеле дурным тоном считается не аннотировать типы публичных деклараций (тех, что экспортируются из модуля). Остальное по желанию.
Да, кстати, в Хаскеле вообще нет различий между лямбдой и именованной функцией, равно как и нет различий между функциональными типами данных и остальными. Более того, так как он тотально ленив, то то, что по типу данных является числом, представляется в памяти как thunk (что-то вроде Supplier в терминах Java). Число вместо него в памяти появляется только тогда, когда понадобилось в других вычислениях.
Да, но если вспомнить, что большинство классов публичные (люди не парятся с internal), то все публичные методы это экспортируемые символы, со всеми вытекающими.

Правда, я не вижу проблем проставлять значения типам всем функциям, чтобы быть уверенным, что в случае ошибки она не пролезла через 10 уровней коллстека откуда-то из кишков.
До рантайм ошибки несовместимости типов не пролезут. Если у функции выведен, а не указан явно тип возвращаемого значения (Unit для функций, декларированных как блок; тип выражения для функций, декларированных как выражения), то это не значит, что его нет.
В любом случае, код, использующий функцию, не скомпилируется, если ожидаемый тип возврата не совместим с выведенным по телу функции.
То же верно и для пулбичных val/var деклараций.
Я не про рантайм. Я про то, что если я поменял код, и что-то сломалось, то я точно знаю, что либо поломался код в функции Х, либо в той функции, которая из Х вызывается. Варианта, что вывод поменял типы у десятка функций, в таком случае, нет.

Вы сейчас описали фичи C#:


вроде перегрузки

Котлин даёт одинаковый API для коллекций и сиквенсов,

в Kotlin нет checked exceptions

Автоматические геттеры/сеттеры

Экстеншн-методы

форма без identity кидает исключение для пустой коллекции.

Все они добавлены для того, чтобы не плодить тысячи строк boilerplate-кода. Вы, случайно, не из тех, кто был против var, ведь нипанятнаже?


вроде перегрузки

Напомню, что в Java есть перегрузка + для строк, как special case, да и [index] имеет логику внутри, а не просто складывает указатели. Вы используется велосипедный конкатенатор строк, чтобы, не дай бог, не пропустить выделение памяти?


Перегрузка гарантирует унификацию. .get(...) / .elementAt(...) / .charAt(...) заменяются на [...]. Наличие обращения к методу вам ничего особо не даёт — в большинстве случаев вы обращаетесь как раз к геттеру / сеттеру, даже если там внутри простое присваивание / чтение поля. В значительной части случаев, реализация скрыта из интерфейсом — знание факта вызова ничего не даёт, в нём может происходить всё, что угодно.


Автоматические геттеры/сеттеры

это страшно

// call getFirstDayOfWeek()

  • Чем может помочь знание того, что при обращении к объекту вызывается метод, а не происходит обращение к свойству с неявным вызовом метода?
  • Во всех языках с перегрузкой полагают, что любое обращение к стороннему объекту — вызов метода.
  • В большинстве случаев, для объектов с поведением, как раз плодятся методы вида get* / set* и унификация доступа к ним снижает количество визуального мусора. x.Width *= 2 вместо x.setWidth(x.getWidth()*2).

Ведь регистр букв системно-зависим

Вы слышали о такой вещи, как инвариантная локаль?


@JvmName("filterValidInt")

Да, костыль над type erasure, чтобы иметь возможноть перегружать методы, как в языках с нормальной vm, а не плодить множество разных имён, но при этом иметь читаемый код в Java, а не генерировать суффикс-хэш автоматически.


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

Это не так работает. Extension-методы — синтаксический сахар, после компиляции они становятся обычными вызовами статических методов, не влияя на целевой тип. Доступа к private / protected членам у них тоже нет.

Extension-методы — для тех, кто в курсе что такое "behavioral mixin". Я бы сказал, что в этом месте автор критикует то, чего не понимает.
В остальном полностью с вашим комментарием солидарен.

Без IDE ничего не поймёшь.

без IDE в любом мало-мальски сложном приложении фиг поймёшь


А IDE плохо, когда ты едешь в поезде и видишь, что свинговый жабоинтерфейс высасывает из ноутбука батарейку как вампир

Jetbrains позаботился о батарейке твоего ноутбука и сделал режим "Power safe mode". Более того — он ещё и настраиваемый.

Без какого IDE? В очень больших проектах, продвинутый редактор может быть даже полезнее IDE и тормозит меньше.
Без какого IDE?

Без любого, умеющего делать подсветку/проверку синтаксиса, форматирование кода и запуск на выполнение. Хотя без последнего тоже можно жить, держа рядом консоль.
Arduino IDE вон тоже IDE, хотя по сути это просто "продвинутый редактор" — даже autocomplete нет

Если речь о понимании проекта, то я бы выделил подстветку, функции Go To Definion/Implementation и поиск использований по проекту(семантический, а не тупой перебор), а так же возможность пробежаться глазами по структуре файла. Поиск, разумеется, ленивый должен быть. Это первое, что пришло в голову.
Настроить всё это достойно в «продвинутом редакторе» дело не лёгкое и проблематичное. И всё, что это даст — аналог IDE с чуть большей скоростью в ущерб функционала, поскольку добиться такого же качества едва ли выйдет.
Извините меня, но в чём смысл статьи? В том, что на котлине можно написать нечитаемую дичь? Дак это и на джаве можно и на любом другом языке.

А вообще в 2018 не писать андроид приложения на котлин это моветон.
Большинство из написанного есть в Groovy, и мне нравится. Половина из этого — плюсы, а не минусы.
Это провоцирует людей писать нечленораздельную лапшу, в которой и ничего не понятно.

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

P.S. А гетеры/сетеры-то чем не понравились, я так и не понял?

Есть интересная статья (и единственная за тоннами хайпа) — Kotlin vs Java The Whole Story. Там сделана попытка обьективно оценить целесообразность перехода на Котлин.

Я очень далёк и от Явы и от Котлин, но вот за это:
Работа экстеншн-методов возможна, даже если автор специально сделал финальный класс, явно показав, что не хочет сторонних расширений.
Котлину можно простить всё. И этого очень не хватает в каком нибудь Go. Ибо меня, как программиста, очень мало интересует мнение автора библиотеки.

Спасибо за быстрое и лаконичное введение в фичи Kotlin-а от лица всех C#-разрабов :)
Как шарпист заявляю: более половины из описанных "странностей" мне очевидны. Kotlin делает так, чтобы писать на Java шарписту стало не отвратительно :) Тут, конечно, есть ещё над чем поработать, но JetBrains движутся в правильном направлении.

У меня такое ощущение, что вокруг Котлина немного искусственно нагнетается шумиха на этом сайте. Нет?

Посмотрите на том же тостере сколько вопросов по Джаве и сколько по Котлину — почти ноль.
Tiobe на днях обновился, Котлин по прежнему рядом с хаскелем на задворках. В целом да, ощущение, что шумиха, в основном, в русскоязычном сообществе. И то лишь потому, что котлинисты громкие, а джависты просто сидят и работу работают.
gradle скрипты кстати начиная с пятой версии можно на котлине писать, все примеры на двух языках идут. Так что определенно есть прогресс и не малый

Дожили! В JugRu теперь платят людям, чтобы они ругали Котлин!

Лютый зашквар,
Трэш и хардкор
Струпья ю-трупа
Жрёт хайпожор.

(с) Louna, «Шум»
А IDE плохо, когда ты едешь в поезде и видишь, что свинговый жабоинтерфейс высасывает из ноутбука батарейку как вампир.
И как часто вы работаете в поезде?
Кстати, об IDE. Насколько хороша поддержка Kotlin в IntelliJ IDEA? Она действительно лучше, чем для Java? Есть большие сомнения.
Ну это аргументация уровня Рен-тв. Совпадение? Не думаю.
Цепочки вроде ?.let { foo(it); }?.let { bar(it); } — это вообще ад

Нормально будет, если в цепочке только один такой оператор. И то от безысходности, если не можешь изменить код библиотечной функции. Рекомендую избегать смешивать nullable и nonNullable типы.
Работа экстеншн-методов возможна, даже если автор специально сделал финальный класс, явно показав, что не хочет сторонних расширений.
Экстеншн методы не про наследование, а про расширение функционала. Финальный класс так и останется финальным. Его никто не наследует и не заберётся в кишки protected.
что будет, если в следующей версии библиотеки автор добавит методы с теми же именами, но с другим возвращаемым типом?
Вам стоит ещё раз почитать документацию. Сработает оригинальный метод.
Да или хоть null вернуть, раз уж это null-friendly язык.

Ага, а потом опять работать с цепочками ?.let { }?.map { }?.filter { }
fun c(check: Int) =

Пожалуйста, не делайте так. Это синтаксис для однострочной лямбда функции.

По пунктам:


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

И? То, что вы не знаете как ведёт себя тот или иной оператор в данном конкретном случае — ваша вина. Язык тут ни при чём. RTFM, наконец!


Без IDE ничего не поймёшь.

А то ж вы в проекте на Java/PHP/C#/C++ много чего без IDE поймёте.


А IDE плохо, когда ты едешь в поезде и видишь, что свинговый жабоинтерфейс высасывает из ноутбука батарейку как вампир.

Ну тут мне остаётся только посочувствовать Java-разработчикам (уж не знаю — то ли потому что на поездах ездят, то ли потому что IDE отжирает столько батареи) :)


Котлин даёт одинаковый API для коллекций и сиквенсов, из-за чего люди злоупотребляют цепочками map/filter на коллекциях, создавая кучу промежуточных неленивых копий.

Меня удивляет ваше стремление решать что другие люди должны делать и чего не должны. Вообще-то это неприлично. Вы злоупотребляете? Нет? Ну вот и ладушки. Остальные, полагаю, сами разберутся без вашего мнения.


Котлин форсит использование it

… а вы всё никак не хотите научиться правильно его использовать facepalm


Цепочки вроде ?.let { foo(it); }?.let { bar(it); } — это вообще ад и должны быть запрещены в декларации о правах человека.

Вот опять, обратите внимание. Откуда в вас это стремление запрещать что-либо всем людям сразу, если конкретно вам непонятна эта конструкция? Избавляйтесь от этой черты характера. Некрасиво это.


От интеропа с джавой кровь идёт из глаз.

Это справедливое замечание. Равно и про компаньоны. Равно как и про type erasure.


А как вам такое: в Kotlin нет checked exceptions.

И в C# нет. И в C++ нет. И много где ещё их нет, потому что checked exceptions — это как раз та бессмысленная часть, которая и делает Java многословной без веских на то оснований. В большинстве реальных случаев вам, признайтесь, по барабану список исключений, которые выбрасывает метод. У вас или все ошибки обрабатываются одинаково, или же вы предпочтёте выбрать пару исключений, которые обрабатываются как-то иначе (а все остальные — всё равно одинаково). Тащить за собой список исключений в этой ситуации не имеет никакого смысла. Практической пользы от него — 0.


Автоматические геттеры/сеттеры

Господи, ну наконец-то хоть у одного JVM-языка появилась эта функциональность. Наконец-то на JVM-языке (вкупе с перегрузками) можно писать point2.Z = (point1.X + point1.Y)*(point2.X - point2.Z), а не point2.setZ(Point.mult(Point.sum((point1.getX(), point2.getX())),Point.subtr(point2.getX(), point2.getZ()))). Да здравствует человеческая инфиксная запись! Ура, товарищи! К 2018 году JVM-язык научился делать то, что человечество использует уже несколько веков.


Экстеншн-методы

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


reduce и fold

Эм… Вообще-то они так работают во всех языках, в которых они есть. В C# вот reduce-а в явном виде нет. Но его легко можно написать. И когда вы это сделаете — вы поймёте почему reduce на пустой коллекции должен бросать исключение.


Нафига в стандартную библиотеку языка, который поддерживает дата-классы, включили пары?

Чтобы вы не захламляли код коммерческого приложения своего работодателя отрядами классов из двух пропертей/полей. Знаете, если вы работаете в команде лоботрясов, то один лоботряс сделает свой Pair для использования в паре мест, второй сделает такой же, третий… И вот у вас уже 10 разных Pair-ов с разными типами и именами, в разных пакетах от разных лоботрясов. Чтобы этой фигни не происходило, люди придумали встроенные в язык туплы (а заодно и деконструкцию классов, просто потому что могут).
Если же вас тянет использовать туплы чтобы заменить ими ВООБЩЕ ВСЁ — вы больны. Не надо проецировать свою болезнь на других. Специально чтобы технически оградить психически нестабильных персоналиев от подобной ереси, встроенные туплы обычно ограничивают 4-8 тип-аргументами. Если вам нужно больше — обычно это говорит о том, что надо делать отдельный промежуточный класс (дата или не дата — это уже вам решать).


Очень странный момент — возможность не указывать возвращаемый тип метода

Очередной привет людям, которые пугаются type inference-а. Идите к группе людей вон там в углу, которые не понимают зачем нужен var. И предложите им оперировать хэшмапой тупл-> список generic-интерфейсов без var и с явным указанием результата работы метода. Ах, вы такого в жизни никогда не писали? Так может имеет смысл чуть по-дольше поработать в индустрии прежде чем критиковать?


Для публичных методов явная спецификация API должна быть

Она и есть. Метод возвращает kotlin.Any. Это очевидно компилятору, очевидно пользователям kotlin, очевидно пользователям библиотек с такими заковырками. Всем, кроме вас.


В общем, автор. Ничего личного, но поднимайте, пожалуйста, квалификацию.

В C# вот fold-а нет

Есть. Он называется Aggregate.
double product = doubles.Aggregate(1.0, (prod, next) => prod * next);

Нет, reduce — это вот этот:


double product = doubles.Aggregate((prod, next) => prod * next);

И это не терминология автора, а функции из стандартной библиотеки Kotlin: fold, reduce

А, значит я их местами перепутал.

Она и есть. Метод возвращает kotlin.Any.

Ну уж нет! Метод с автоматически выведенным типом результата возвращает этот самый автоматически выведенный тип, но никак не kotlin.Any.

Нене. Я подозреваю что там type inferer ищет максимально общего предка для всех возможнных return-значений. Если, как автор в своём примере, мы подпихиваем совершенно несовместимые типы, то в результате разумно предположить что будет kotlin.Any. То есть это было сказано про данный конкретный случай, а не вообще. Хотя надо пощупать это поведение.
Иными словами, kotlin не запрещает стрелять себе в ногу. И это правильно.

Ох уж эти проклятые геттеры/сеттеры, спать не дают))
        //java
        int sum = point1.getX() + point1.getY();
        int subtr = point2.getX() - point2.getZ();
        point2.setZ(sum * subtr);

        //kotlin
        var sum = point1.x + point1.y;
        var subtr = point2.x - point2.z;
        point2.z = sum * subtr;
//Java
point2.setZ((point1.getX() + point1.getY()) * (point2.getX() - point2.getZ()));

//Kotlin
point2.z = (point1.x + point1.y) * (point2.x - point2.z)

Если вам нужны локальные переменные только для упрощения чтения кода — это уже о чём-то да говорит.
Если они нужны где-то ещё далее по коду, то пусть будут конечно. Только в Kotlin рекомендуется использовать val.

Ребят, обратите внимание что мой пример — он демонстрирует акцессоры вкупе с перегруженными операторами. X/Y/Z в общем случае могут быть не числами.

В данном моменте я как раз за Kotlin.
А уж если предположить что поля Point не наследники Number, то в Java и математические операторы использовать нельзя.
В случае такого варианта:


//Java (и то только если X/Y/Z иммутабельны)
point2.setZ(point1.getX().add(point1.getY()).multiply(point2.getX().subtract(point2.getZ())));

//Java (с переменные для "читаемости")
CustomNumber sum = point1.getX().add(point1.getY());
CustomNumber subtr = point2.getX().subtract(point2.getZ());
point2.setZ(sum.multiply(subtr));

//Kotlin
point2.z = (point1.x + point1.y) * (point2.x - point2.z)

Что в первом что во втором варианты Java-кода я не могу сходу опередить что там происходит — код приходится парсить глазами.
При этом Kotlin-код читается без проблем.

А как себя поведет этот пример в Котлине, если x/y/z «не наследники намбера»? Неужели «объект какого-нибудь класса» + «объект какого-нибудь класса» скомпилируется?
какой то синтаксический сахар… Наверно мне не понять все преимущества этой перегрузки операторов)
Преимущества этого сахара описаны 4 комментариями выше.

Это вы, просто, мил человек, не писали адовой математики/геометрии на ОО-языках. Ну или даты не складывали...

По содержанию статья похожа на написанную человеком, который до Kotlin видел только Java.

Очень странный момент — возможность не указывать возвращаемый тип метода (особенно публичного).

fun width() = right - left

Цепочки вроде ?.let { foo(it); }?.let { bar(it); } — это вообще ад и должны быть запрещены в декларации о правах человека.

В конкретно этом примере правильнее скорее будет ?.let(::foo)?.let(::bar).

Хотел проехать по всем пунктам, но не стал. Напишу лучше только что мне самому не нравится в этом языке:
1. Такая форма записи не сработает val color: Int = 0xFFFFFFFF.
2. Наличие type-erasure доставшееся от того же JVM.
3. Ограничения с наследованием data class-ов.
3. Конфликты с методами-сеттерами и обычными полями с сеттерами.
open class Foo {
fun setValue(value: int) {}
}
class Bar: Foo() {
var value = 10
}


После Java язык как глоток свежего воздуха.
Каждый раз, когда я читаю про краткость и лаконичность кода, вот честно хочется плакать и менять проффессию. Все программисты представляются хакерами из кино, которые 8 часов в день фигачат код со скоростью 1000 знаков в минуту.

Но если б это было так, то в вакансиях первым делом висело скорость печатания — от 800 знаков в минуту, скорость чтения от 1000 слов в минуту.

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

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

Банально с = a>b?a:b — это гораздо хуже, чем с = if(a>b) a else b; хотя тут и букв больше.

Есть определенные пределы сокращения кода, когда он перестает быть читаемым. В идеале скорость чтения должна совпадать со скоростью понимания. Потому что в мозгу вы все равно развертываете тернарную конструкцию в if и на это тратится ценное время.
Каждый раз, когда я читаю про краткость и лаконичность кода, вот честно хочется плакать и менять проффессию. Все программисты представляются хакерами из кино, которые 8 часов в день фигачат код со скоростью 1000 знаков в минуту.
Дело не в скорости написания кода. Дело в скорости и простоте его чтения.
Злоупотреблять конечно не стоит, но когда вы не можете написать что-то без boilerplate в 3 раза длиннее полезного кода — это ещё хуже.

Потому что в мозгу вы все равно развертываете тернарную конструкцию в if и на это тратится ценное время.
На своём коде я привык так, что тернарный оператор — по определению простой и односложный, максимум одно сравнение и одна операция. А если встречается if, значит дальше пойдёт какая-то жесть, и надо переключаться с быстрого чтения на вдумчивое.
Есть DataObject на java в нем 20 полей, какой правильный способ заполнить их нулями? Длинный конструктор, череда сеттеров, или рефлексия?
Интересный вопрос, начиная с того, что вы имеете в виду под словом «правильный» и заканчивая кучей неназванных «мелочей», которые могут диаметрально развернуть оценку.
вопрос собственно в том что лучше: написать двадцать тупых сеттеров, или рефлексией пробежаться по полям ставя им нолики.

Первый — ненавистный бойлерплейт, но простой как лом и пишется и читается, второй посложней, но увидя код надо напрячь мозги, чтоб понять что он делает, а если с рефлексией давно дел не имел, то скорей всего придется гуглить синтаксис, чтоб написать. Но покороче и потяжелей, но более гибок, не надо дописывать метод, если добавилось новое поле.

Что правильней писать?

Третий — с помощью рефлекшна пробежаться по полям и скомпилировать делегат, который будет делать нужную работу с качеством написанного ручного кода. Когда-то делал такую штуку, чтобы сравнить все поля двух объектов в тестах.


Четвертый — сделать нормальное АОП, где просто можно атрибутом повесить #[derive(Default)] и получить то же самое от компилятора.

вопрос собственно в том что лучше: написать двадцать тупых сеттеров, или рефлексией пробежаться по полям ставя им нолики.
Как я уже говорил, зависит от…
А если есть поля, которые надо не трогать?
А если правило заполнения может поменяется?
А если типы у полей разные?
А может ли сигнатура у объекта поменяться и если да, то надо ли новые пполя тоже забивать нулями.

Кроме того, использование 20 тупых сеттеров, — это не бойлерплейт.
Бойлерплейт это объявление 20 тупых сеттеров. То есть у вас тело объявления дата-класса разрастается с 20 строк до 140, если делать всё «по феншую».

ИМХО, в данном случае лучше вообще сделать для объекта метод clear и сохранить значения полей напрямую.
Если вы его создаете через new, они и так заполнены нулями
единицами или строками «тут ничего нет»)))
Ну тут ничего не поделаешь, нужно делать memset из единиц с начала раскладки объекта в оперативной памяти и до максимального оффсета :))

Делаете конструктор и заполняете нулями.


По-умолчанию дефолтным состонием объекта можно быть что угодно. Вон, вам "null" например не подходит, хотя именно это дефолтное состояние для типа "ссылка". Вам подавай проинициализированную ссылку.


Учитывая, насколько задача искусственна, нет смысла надеяться на встроенный способ решения.

Вы так все фичи записали в баги.


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

Всего лишь синтаксический сахар к вызову функции. Позволяет писать более лаконичный, но менее понятный код. Как и автовывод типов. Как и многие другие фичи. Но да, для их использования нужно знать язык, чтобы код был понятным.
Есть, конечно, и сомнительные фичи, но совсем не так много, как вы пишете.


Автоматические геттеры/сеттеры с добавлением английского слова get и первой буквой проперти в большом регистре (видимо, в локали ENGLISH? Ведь регистр букв системно-зависим)

Что значит "системно-зависим"? Не слышал, чтобы .toUpperCase/.toLowerCase работали как-то, отклоняясь от правил юникода.


Без IDE ничего не поймёшь. А IDE плохо, когда ты едешь в поезде и видишь, что свинговый жабоинтерфейс высасывает из ноутбука батарейку как вампир.

Высасывает батарейку не "свинговый жабоинтерфейс", а разбор кода и куча инспекций. Вы же используете эти IDE не из-за любви к странным интерфейсам, а потому, что они помогают решать задачи?

.toUpperCase/.toLowerCase в Java зависят от локали, см тут:


Note: This method is locale sensitive, and may produce unexpected results if used for strings that are intended to be interpreted locale independently.

И правда. Однако по вашей ссылке есть также совет:


To obtain correct results for locale insensitive strings, use toUpperCase(Locale.ROOT).

Полагаю, разработчики языка учли это.

Оказалось, в C++ можно не писать return в методе, который согласно сигнатуре должен что-то возвращать. Это не синтаксическая ошибка согласно стандарту, а undefined behavior. Соответсвенно, программа в рантайме падает с произвольной ошибкой.

Я буду читать предупреждения от компилятора
Я буду читать предупреждения от компилятора
Я буду читать предупреждения от компилятора
Это не предупреждение должно быть, а ошибкой.

Настраивать же «варнинги как ошибки» получается нужно очень дотошно, потому что список варнингов многие тысячи, и все их нужно просмотреть, и понять, какие из них на самом деле должны были быть ошибками.

По сути, претензия к тому, что для подобной «фичи» выбран неверный уровень опасности. Это не просто «предупреждение», это полноценный баг в 100% случаев.

— Бтв, интересно, какие ваши действия, когда вы приходите на новое место, а там сборка проекта выплевывает тысяч 10 ворнингов? Переключаете по одному на ошибку раз в неделю на протяжении 25 лет?
Ворнингов быть не должно. Если они есть — перед вами кусок говна.
Ну окей, тогда любой проект, который существовал пару лет до моего прихода — кусок говна. Только делать-то что с этим?

Смириться с этим и по мере возможностей заворачивать в фантики

Объяснить руководству и править.
Что объяснить? Варнинги есть пить не просят. Да, периодически что-то взрывается, и приходится чинить. Руководство честно взвесит стоимость периодических исправлений то тут, то там, и переписывания всего «по-уму», и примет решение «а хрен с ним, проще жить, как живем». Особенно если услужливые коллеги скажут, что эти варнинги всегда были, что результат от их переписывания может быть, а может и нет, а «косты» будут точно…

Как-то так.
Для начала нужно сравнить «косты» исправления багов и добавления новых фич.

Если, несмотря на предупреждения, основнове время таки-реально уходит на новые фичи — то тут можно только снять шляпу и удивляться.

Но обычно если ворнингов тысячи, то «взрывается» постоянно и чинится 90% времени. А на новые фичи остаётся 10% времени. И тут можно уже что-то обсуждать.

P.S. Конечно перед этим нужно потратить какое-то время на анализ. Потому что сами такие компании бывают абсолютно уверены в том, что у них 100% времени уходит на новые фичи. Но если копнуть и выяснить почему некий Вася добавляет иконку не час, а две недели — то выяснится, что бóльшую часть времени — он чинил баги, которые проявились, когда он оную иконку добавил. И вот это-то время нужно отделить от времени добавления новых фич.
Это не предупреждение должно быть, а ошибкой.
К сожалению C++ не Java. В Java — это ошибка. В C++ — нет.

— Бтв, интересно, какие ваши действия, когда вы приходите на новое место, а там сборка проекта выплевывает тысяч 10 ворнингов?
Как правило из этих 10 тысяч 90% — это какая-нибудь непринципиальная чушь, которую можно отключить.

Типа использования ols-style cast вместо static_cast/const_cast/reinterpret_cast.

Но вообще — ворнингов быть не должно. Если вы какую-то вещь не хотите энфорсить (скажем обработку всех вариантов enum'а во всех switch'ах), то эти ворнинги нужно отменить. -Werror можно (и нужно!) выключать только в релизе (так как не всегда известно каким компилятором его будут собирать), во время разработки он должен быть включён.
К сожалению C++ не Java. В Java — это ошибка. В C++ — нет.

То что С++ это разрешает, не делает это ошибкой в меньшей степени :) Ошибка по сути в спецификации С++, которая разрешает такое поведение.

Как правило из этих 10 тысяч 90% — это какая-нибудь непринципиальная чушь, которую можно отключить.

Для этого их надо все проанализировать, а на это обычно никто не соглашается, и так работает ведь, и вообще отключи их показ и все нормально (с).
— Бтв, интересно, какие ваши действия, когда вы приходите на новое место, а там сборка проекта выплевывает тысяч 10 ворнингов?
Увольянюсь из такой говноконторы.

Думал некоторое время, отвечать или нет на очевидный вброс, тем более, что выше уже отписались. Решил, что добавлю то, чего выше, как мне показалось, недостаточно расписали.


Котлин даёт одинаковый API для коллекций и сиквенсов, из-за чего люди злоупотребляют цепочками map/filter на коллекциях, создавая кучу промежуточных неленивых копий. Стримы в джаве специально введены для различия между ленивой и неленивой коллекцией. Да, есть инспекция в IDE для этого — потому что инспекции призваны исправлять недостатки языков.

С этим можно согласиться. С другой стороны, тот же C# неявно аллоцирует внутренние коллекции во всяких GroupBy/SortBy, но не помню, чтобы на каком-то проекте это вызывало проблемы. Читаемость и корректность важнее, ради этого собственно итераторы и придуманы.


Котлин форсит использование it, что приводит к нечитаемому коду. Что-нибудь типа seq.map { it -> foo(it, 1); }.map { it -> bar(it, 2); }.filter { it -> it.getBaz() > 0; }.

Но вы его почему-то не используете… Кстати, тут косяк котлина, что он не сказал


  • что можно написать короче
  • что вы используете ключевое слово как имя переменной

Цепочки вроде ?.let { foo(it); }?.let { bar(it); } — это вообще ад и должны быть запрещены в декларации о правах человека. И это считается идиоматично, Карл. В отличие от нормального if. Читать такой код невозможно.

Я может чего-то не знаю, но разве в котлине нельзя создавать промежуточные локальные переменные?


От интеропа с джавой кровь идёт из глаз. А тут всякие JvmStatic и JvmName, и код превращается в цирк с конями.

Не пишите на котлине библиотеки для джавы, вот и все. Пишите на котлине приложение, и те библиотеки, которые будут использоваться только из котлина.


То же относится к вопросу сеттеров (и вообще всего интеропа)


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

Я правильно понимаю, что если автор библиотеки не предусмотрел метод swap, то нам убиться теперь, потому что нам не разрешено написать свою реализацию? А если нам можно написать свою реализацию, нам прям обязательно её вызывать через ListHelper.Swap?


Библиотека местами не продумана. Например, reduce.

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


Давайте ещё навалим про библиотеку. Нафига в стандартную библиотеку языка, который поддерживает дата-классы, включили пары? Это ж прямое поощрение плохого кода.

Вот на это хотел возразить особо.


Вот код, который я написал буквально вчера. Это обработчик телеграм-бота, который пытается понять, была ли в сообщении какая-нибудь картинка, и если есть, пытается получить id картинки в наилучшем разрешении:


let processing_info = match (&update.message.from, &update.message.document, &update.message.photo) {
    (Some(ref from), Some(ref document), _) => Some((from, &document.file_id)),
    (Some(ref from), _, Some(ref photo)) => photo
        .iter()
        .max_by_key(|x| x.file_size.unwrap_or(0))
        .map(|x| (from, &x.file_id)),
    _ => None,
};

let (user, file_id) = match processing_info {
   Some(x) => x,
   None => {
      info!("No values!");
      return;
   }
}

Вы правда считаете, что мне стоило вводить на верхнем уровне тип UserWithFileId, просто чтобы вернуть пару значений?


А знаете, что бы вы сделали в java мире? Вы бы объявили пару отдельных переменных, присвоив им null. Потом написали бы кучу ифов, которые при верном значении перезаписывали бы соответствующий null на значение. После всего этого вы бы проверили что из этого null, ну и дальше какие-то действия… В итоге превратилось бы это в простыню раза в 2 длиннее, и ненадежнее из-за перезаписи.


Очень странный момент — возможность не указывать возвращаемый тип метода (особенно публичного).

С этим согласен




Заключение: большинство из этих "багов" — фичи, без которых рождаются AbstractAnnotationConfigDispatcherServletInitializer

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


Вот, допустим, мне нужно найти произведение шести чисел, и я решил использовать reduce. Мне в ответ вернуло Optional — что мне с ним теперь делать?


Можно, конечно же, дописать какой-нибудь .orElse(1) — но подобный код подразумевает, что коллекция из шести чисел может оказаться пустой! А если бы она могла оказаться пустой, я бы использовал fold вместо reduce. Поэтому я не будут вызывать .orElse(1), я вызову .get() — и получу для пустой коллекции то самое исключение, от которого меня хотели уберечь.

Вот, допустим, мне нужно найти произведение шести чисел, и я решил использовать reduce. Мне в ответ вернуло Optional — что мне с ним теперь делать?

Что угодно. Можно вернуть orElse и получить значение в любом случае. Да, я возможно ожидаю, что элементов может не быть, и хочу предусмотреть это в логике. Писать же логику на try catch плохая идея. В итоге я в любом случае не могу нормально воспользоваться стандартным методом без логики к catch.


А если бы она могла оказаться пустой, я бы использовал fold вместо reduce.

Ну так, reduce([]) в таком случае вернет None, reduce([0]) — 0. А fold(0, []) вернет то же, что и fold(0, [0]) — ноль. А мне может быть важно уметь различать эти случаи.

Функция reduce нужна для тех случаев, когда коллекция не может быть пустой. Никакой логики на try/catch писать не требуется. Если нужна какая-то "логика" — нужно использовать fold.


А мне может быть важно уметь различать эти случаи.

Тогда пишите fold(None, ...) и настанет вам щастье!

Тогда пишите fold(None, ...) и настанет вам щастье!

Тогда в редьюсере аккумулятором будет Maybe, что делает его использование не таким удобным. Сравните fold(|acc, x| acc + x) и fold(None, |acc, x| acc.map(|value| value + x)).

Если писать такое однократно и в режиме write-only — не вижу ничего страшного. В конце концов, делал я в однострочниках асинхронный цикл на промисах через reduce и не никто не умер от этого…

Если такое нужно часто — можно и свой хелпер завести.
Ну никто не говорит, что это прямо ужас. Просто неудобно.
Наверное, это раст мне мозг перестроил. Я теперь на эксепшны смотрю с ужасом)

Вот только что-то я не могу в документации на std::iter::Iterator найти аналог fold без указания начального значения аккумулятора...

Ну вот например.

Стандартный итератор не все полезные вещи умеет.
Что-нибудь типа seq.map { it -> foo(it, 1); }.map { it -> bar(it, 2); }.filter { it -> it.getBaz() > 0; }.
Кстати, тут косяк котлина, что он не сказал
  • что можно написать короче
  • что вы используете ключевое слово как имя переменной

Это не косяк Kotlin-а — это, в лучшем случае, отсутствие варнинга со стороны IDE.
it это не ключевое слово, а дефолтное имя для переменной, попадающей в ламбду, если это имя явно не переопределить самому. И никто не запрещает использовать it где-либо ещё (что как раз является одним из ограничений ключевых слов) и, уж тем более, никто не запрещает использовать его в качестве имени переменной указанной руками.
Но инспекцию на такое поведение со стороны IDE было бы не плохо иметь.

>Боевые протезы

Приятно видеть, что кто-то ещё помнит творчество группы «Беломорс».
Начал знакомство с ними после «Ночного Дозора» :)
в C++ можно не писать return в методе, который согласно сигнатуре должен что-то возвращать

Меж тем у компилятора (gcc по крайней мере) есть ключик -Werror=return-type, превращающий соответствующий warning в полноценный error. Очень рекомендую.

Вот тут хорошо сказали про ключи компилятора относительно варнингов

Апплодирую стоя! на 100% согласен с автором если не в деталях, то в сути поднимаемой им проблемы.

Поиск очередной серебрянной технологии пули, помноженный на современные технологии пиара и очковтирательства в купе с увеличением «поголовья хомячков в IT» приводят к действительно безумной ситуации.

И это проблема, т.к. ситуация приносит выгоду только желающим «погреть лапу» на семинарах/пиаре/продажеКнижОнок.

Появление новых фреймворков и языков практически не имеет никакого отношения к решению технических проблем, которые стоят перед разработчиками.
... а проблемы как валялись так и валяются.
Стада хомячков гоняются от одного «синтакс-сахорочка» к другому; новые языки придумываются потому, что кому-то не нравится _синтаксис_, а за основу берутся «примитивы» для обучения дошкольников программированию; потом эти «go-go»-новорожденыши пихается в продуктив и «буквальновсе» пиарятся что вот уже «целый гугл» использует «go-go» (ну и конечно же как самый основной язык для самого центрального сервера, а не на задворках для автоматизации скриптов подготовки данных для юнит-тестов); толпы неофитов бьтся в экстазе от того, что на эксклюзивных курсах «от новичка до профессионального программизда за 3дня» освоили очередную сакральную концепцию применимую в одном единственном языке/фреймворке; IDE выбираются не за поддержку тех или иных возможностей, а за темную или светлую тему оформления; дизайн и работу элементов интерфейса определяют «груше-дизайн-ХУДОжники», которые никогда не будут пользоваться своим «творением» за пределами презентации;… (продолжать?).


О том, что это всё — ни разу НЕ НОРМАЛЬНО — надо периодически напоминать.

Потому, что, согласитесь, задача языков программирования и it-технологий — это всё таки не радость неофита от прикосновения к сакальному и его вкусовые очущения от синтакс-сахара, а решение технических задач, однозначность кода, обеспечение прогнозируемости поведения и прозрачности структуры системы.

PS
PS: Кстати, пока вы читали этот пост, и задавались проблемами а «что же с этим всем делать», ещё 2 школьника «изобрели» «2 уникальных инновационных js-фреймворка», технологию, парадигму разработки и под это дело — целый язык программирования — с турбореактивным связыванием, интеллектуальным дополненеим кода в рантайме с угадыванеим «что имел в виду заказчик», с удобным безскобочным принудительным форматированием кода отступами, компиляцией в node.js-байткод и нивуя-себе-шаблонизацией. «Как сейчас но ещё лучше». Надежно, масштабируемо, проверено. Обкатано на 3-х лабораторных работах. 10ю коллегами автора. Уже затерроризирован учитель информатики, который будет на нем делать школьный сайт и воодушевлено 3 журналиста. Скоро будет репортаж про дизактиватор Бубкина с веб-интерфейсом на первом канале.

И это все станет популярным через 2 года. На пару месяцев. И десятке-другом продуктовых серверов. Хорошо, если только этим все и ограничится, и не нам с вами это поделие разгребать и поддерживать, после того как их авторы срулят, заметая следы, в туман рубить бабла на впаривании очередное откровения. Или просто отмахиваясь куда-подальше от монстра который они создали.


Хочу поопонировать вот тут. Если неофиту не нравится писать на языке, он им пользоваться не станет. Через какое-то время старый язык просто умрет вместе с поколением предыдущих пользователей. Сценарий вполне реальный, только вот он не кажется… справедливым, что ли. Если уж ты что-то приручил — например, написал свой язык и специально сделал его популярным — наверное, стоит о нём заботиться.
Не соглашусь. Задачи языков программирования — очень разные, и уж точно не регламентируются третьими лицами в кулуарах обсуждений на тематических форумах.
С другой стороны, Kotlin позволяет писать гораздо быстрее. А еще у него из коробки идет nullSafety, что автоматически принуждает разработчика думать о том, что и куда он сует. И много других вещей, позволяющих писать более гибкие и продуманные сценарии поведения приложений.
При этом никто не кричит, что то же самое можно сделать на Java — просто в Java nullPointer пропустить можно по невнимательности, а в Kotlin — намеренно приколотив гвоздями неопциональное значение. Заметьте, никто не навязывает парадигму — хотите, пишите по старому. Более того, хотите, юзайте Java — она тоже совместима с Kotlin. Нет — все равно будем топить любое новшество просто потому что «у меня есть инструмент, я им решаю свои задачи, изучать новые инструменты мне не надо».
Задачи языков программирования — очень разные, и уж точно не регламентируются третьими лицами в кулуарах обсуждений на тематических форумах.

гм… при чем тут «регламентирование»?
мы обсуждаем вектор классификации языков программирования, которые претендуют на «хоть сколько то» практическое применение. надо же как то оценивать и сранивать направленность языков и возможность их применния?)

и где и как вырабатывать такую классификацию, если не в обсуждениях между специалистаи в паблик форумах (хабр же не кулуарное обсуждение, согласитесь?)

С другой стороны, Kotlin позволяет писать гораздо быстрее. А еще у него из коробки...
стоп стоп стоп ))) пАгадите… при чем тут котлин? я нигде не наезжал на котлин, и не упоминал его, ведь правда? заметьте.

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

я рад за тех кому это нравится. серьезно. но мне, признаться, несколько «все равно», из коробки там пара фич или нет… ну честно, извините. я настолько наслышан от предыдущих проповедников о прелестях их новых языков, и настолько видел, «куда эти языки ушли» вопреки рекламным проповедям, что простите, я вам, конечно, верю, но в вашу веру обращаться не буду, как минимум пока не будет на это технической необходимости. а технических оснований я не вижу — НАМ переводить джава проекты на котлин дорого и рискованно. не те ставки. не курсачи пишем.

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

и если говорить про котлин и моё «имхо»(если вы пытаетесь меня в чем-то убедить) то вот вам мое имхо: извините, не обижайтесь, но я не вижу чем котлин принципиально отличается от другиз jvm-языков.

ну вот сами посмотрите:
* был груви? был. где он? мода прошла, груви уходит на обочину истории оставясь узким нишевым языком для гиков.
* была скала? была. где она? мода прошла, скала уходит на обочину историию оставаясь нишевым языком для гиков.
* был клюжЮр? был… где он?..
и так далее — там еще в ряду, если верить википедии jPython, jRuby и далее…

и котлин ничем принципиально о них не отличается. извините, имхо.

если в чем и есть отличие ситуации с котлином, так это только то, что гуглю нужен «запасной аэродром» из-за постоянных нападок оракла. ни больше ни меньше, и это ни разу не связано с прелестями языка. это ситуация, «так карты сложились». они плотно дружат c jetBrains и «почему бы нет», я полностью понимаю выбор гугля…

но прошу вас, не надо рассказывать про прелести языка… если завтра гугль и оракл достигнут мирного соглашения — котлин с 90% вероятностью пойдет на обочину истории, как и его предшественники. в нишевость, в гиковость. и там ему пусть будут рады, я рад за тех кому это зашло.

все равно будем топить любое новшество просто потому что «у меня есть инструмент, я им решаю свои задачи, изучать новые инструменты мне не надо».
«не надо навешивать на меня ваших крокодильчиков» ) я такое не говорил.
я за разумные новшества, обоснованные, не приводяшие к технической неоднозначности или непонятности для разработчиков. я ЗА новшества. но за взвешенное к ним отношение.

давайте поясню на примере джавы: диамонд синтаксис — классная вещь, потому что понятно
«что пропускается» и где смотреть что пропущено, код по прежнему однощначен. это было хорошее нововведение.

А вот лямбда функции — «полнейшая фигня»)) очень сомнительно. потому что без IDE, читая текст примера кода на сайте — вы можете только догадываться о типах передаваемых параметров. это было плохое нововведение. из исходного кода делись определения тип аргументов и какие они долджны быть — из текста исходного кода не понятно.

Или вот взять проблемы перехода с 6й джавы на 7ю. Болезненно для многих, особенно банковских и др интерпрайз систем, да. Но извините, доступ к приватным методам класса через рефлекшен — это не просто не правильно, это не описано в стандарте, и по сути, сродни багу платформы. Это и убрали в 7й джаве, но сохранили обратную совместимость в рамках описанного в стандарте поведения.

понятно мое отношение к новшествам? )

(а вот теперь я буду наезжать на котлин )
Кстати, где у очередного убивцы джавы котлина — стандарт языка, описание поведения, процесс сертификации сторонних компиляторов на предмет совместимости со стандартом? котлин, вопреки заявлениям авторов языка, не сможет заменить джаву, как минимум в интерпрайз секторе, пока не сможет предоставить аналогичных стандартов и воплотить гарантии сохранения обратной совместимости в виде явного стандарта и процесса сертификации сторонних компиляторов.

извините — я это у всех проповедников новых языков программирования спрашиваю, вы не первый.

не обижайтесь, но давайте с котлином подождем года 2 или 3. вдруг там оракл и гуглем помирятся?)
или вы согласитесь со мной, в моем понимании, "почему джава успешно пережила не один сезон «убивцев джавы», и почему котлин, сам по себе, ни разу не исключение". или котлин займет нишевое положение эксклюзивно-платформенного языка, так же как свифт с иосом) — посмотрим, время покажет, не торопитесь, в общем.

вам заходит, — классно. пилите свои проекты, глядишь толк из этого выйдет. но не надо агитировать моих стажеров или меня. и хайп вокруг языка — ну прошу, не надо вот этого.., столько раз уже было, ну надоело, честное слово… глупое это дело… да и показывает вас с не лучшей стороны.
мы сами к вам придем коли потребуется.
спасибо что дочитали. удачи вам.
Первый пост просто взрыв ниже поясницы, потом 2 страницы о том, как ты за кого-то радуешься, а в конце «мне в общем то все равно». Сильно, очень сильно.
Если ты в треде о котлин пишешь длинную тираду «я не о котлине вообще писал» — дальше можно не распинаться, слушать ужа на сковороде никто не будет.
Я конечно дико извиняюсь )) но вот разве в коментарии к статье о котлине нельзя написать, что "автор на примере конкретного перечня проблем в своей стаье, на самом деле иллюстрирует серьезную (мега)проблему _вообще_, которая имеет место быть не только с котлином"?

Именно это я в первом посте и написал. разве нет?
а вы начали воспринимать это как оскорбление колтина, попытку регламентирвоать назначение языков, порывались засыпать меня перечнем «фич из коробки»… ну и зачем так? ))

ладно. проехали. Нравится обсуждать конкретику, без попыток сопоставить с ситуацией вообще за пределами отдельно взятой узкой темы — ок, я не против, но я не с вами в данном направлении. Мой комментарий совсем не про конкретные микрофичи языка. не поняли друг друга, бывает.
UFO just landed and posted this here
Есть некоторые приколы. Например == использует equals, который работает совсем не так, как ожидает пользователь, например для BigDecimal. Поэтому BigDecimal("1.00") != BigDecimal("1.0"). В принципе не существенная проблема, но могли бы и обёртки для таких случаев добавить.

Тут всё как раз просто и логично и проистекает из начального тезиса: == использует equals
Всё остальное зависит от реализации equals в конкретном типе.
В данном случае эти действительно два разных числа и, кстати, они не равны по equals с BigDecimal("1"), и пользователь, раз уж использует специфические классы, должен об этом знать.
Это не проблемы языка.

UFO just landed and posted this here

В общем случае Comparable это про порядок, а не про идентичность и никто не гарантирует что если x.compareTo(y) == 0, то и x.equals(y) == true.
В JavaDoc Comparable.java описание контракта декларирует It is strongly recommended (though not required) that natural orderings be consistent with equals.
Так что использовать результат compareTo для идентичности так себе идея и правильно что её не используют.


А обёртки могут и появиться в следующих версиях или уже присутствовать в библиотеках. Это мало связано с языком как таковым.

А как быть с теми же Double? которые и в Comparable могут "врать" и приходится указывать допустимую погрешность вида "d1-d0<0.01", при которой мы считаем, что эти два объекта ещё "равны".
Так что да, "==" есть equals и как уж там реализует его конкретный тип — проблема этого типа, а не языка.

В статье легко можно заменить жаву на vb, а котлин — на c#, получится то же самое
Точно так же, когда в доднете появились лямбды и var, народ выл, что код станет непонятным. Прошло сколько-то лет, и уже никто не мыслит себе разработку без них, хотя вот как раз таки с дотнетовскими closures выстрелить себе в ногу проще простого

Лично для меня переход с c# на жаву был крайне болезненный, после .net 4.7 смотреть на откровенно убогие дженерики и ужасные streams было мучительно больно. Плюс совершенно безумная verbosity жавы — каждый раз, когда я писал код, возникало ощущение, что обьясняю что делать умственно отсталому ребенку.
А вот котлин стал натурально лучом света, причем некоторые вещи сделаны даже лучше чем в дотнете.

Иными словами, котлин — удобный и да, сложный тул для профессионалов. Но ставить ему это в в недостатки — это примерно как винить автомобиль за повышенную опасность в сравнении с телегой на конной тяге.
А вот котлин стал натурально лучом света, причем некоторые вещи сделаны даже лучше чем в дотнете.
не холивара ради, а интереса для. А какие?

Вот мой список, в порядке от более желаемого к менее:


  1. Инлайн-лямбды;
  2. Null Safety;
  3. дата-классы, которые в C# уже четвертую версию языка обещают да ввести не могут;
  4. Delegated Properties;
  5. Неизменяемые переменные (val);
  6. Корутины без await.
Null Safety;
Вот из-за этого я очень жду C# 8.0
Корутины без await.
Вот это выглядит как костыль, как по мне. Если это так классно, то зачем IDE отдельно помечает такие вызовы?
Да пусть лучше их IDE помечает, чем я сам и вручную! Впрочем, и на последнее место эту фичу я тоже не просто так поставил.
Вот из-за этого я очень жду C# 8.0


в котлине null safety реализована через, вобщем-то, отсутствие классического null. В К. это обьект типа Nothing?, т.е., можно смело писать null.toString(), например
Интересно, как дотнет с этим справится
null.toString()
в obj-C в купе с его динамической типизацией это вызывало проблемы в рантайме.

C# пошёл по пути swift, в проекте можно поставить флаг, и все переменные станут не нулабельные, пока явно не скажешь это. string? s = null;
ну это ж явный костыль
не знаю, с моей точки зрения — Nothing, как вы его описали, выглядит как костыль. Хотя вот в этих примерах он интереснее
с моего имха — это скорее к давнему спору null vs object
Они решили проблему в пользу(частично) object. T.e., null — это обьект. В то же время, это часть системы типов, часть языка.
Флаг же в проекте(как в свифте) вызывает у меня ассоциации с древним рантайм кастом в с++(dynamic_cast), когда надо было подключать доп. библиотеку, чтоб его делать:)
хм. Смущает сам флаг? ну так есть тонна кода, которая написана без учёта этой особенности. В свифте флага нет.

Ну и вот в этой презентации про null safety в Котлин как раз описывается то, что будет в С# 8 и как это есть сейчас в Swift…
www.hwsw.hu/kepek/hirek/2017/08/hwswfree_20170829/03_Farago_Janos-nullsafety_in_kotlin.pdf
ну, я в общем представляю, как оно в котлине, я на ем пишу:)
Флаг меня смущает тем, что это какая-то внешняя проверка, на которую, вобщем-то, полагаться нельзя. Один дев будет кодить с флагом, второй без, и либо у одного проект не будет компилироваться, либо вылезут артефакты на рантайме.
Один дев будет кодить с флагом, второй без, и либо у одного проект не будет компилироваться, либо вылезут артефакты на рантайме.
Эм… проект-то тоже в системе контроля версий. Если один включит, а другой выключит, то не будет компилироваться у кого-то. Ну, если один будет на одной версии языка писать, а другой на другой — тоже самое получится.

Флаг нужен для того, чтоб braking changes не ломали проект при переходе на новую версию языка (как это у свифт постоянно выходит). Никто не хочет рефакторить старый код, который работает.
Никто не хочет рефакторить старый код, который работает.


какая тогда польза от этого флага?
Для нового кода, для кода который готовы рефакторить. Я постараюсь включить это в своём проекте.
ну а с К. ничего включать не надо, все уже есть.
Просто в K она была изначально и нет тон легаси кода?

Включили галку, внутри вашего проекта все фичи есть, если что-то приходит снаружи, то оно nullable. всё то же самое, что в K, просто существующий код не сломается, если вы этого не захотите.

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


под «снаружи» я имею в виду жава классы в том же проекте/пэкедже. Проект один и тот же, иначе говоря
в .NET нельзя просто так взять и писать исходники сразу на двух языках. Их придётся по разным проектам разложить.

А объекты из подключённых библиотек, тоже будут опционалами, если библиотека не поддерживает данную фичу.

Ещё раз, галка нужна для тех, кто хочет использовать новые фичи c#, новый компилятор и рантайм, но не переписывать существующий код на опционалы.
в .NET нельзя просто так взять и писать исходники сразу на двух языках


Я знаю, и именно об этом и говорю. Перейти с упоминавшихся миллионов строк легаси на жаве на котлин иначе было бы просто невозможно, никто б не стал так рисковать. А так — берешь старый прожект и просто пишешь новые фичи на новом языке. Ну или конвертишь старые классы в К. — это несложно
вот у вас такой галочкой является сам язык.

А тут, уж простите. И легаси код на c# и новый на C# как понять где string s, может принимать null, а где нет? Галкой: вот в этом проекте string s — нублабельно, а вот в том изволь писать string? s;

пишешь новый код в новом проекте, или рефакторишь старые классы — это не сложно.
очень просто, если прожект на жаве и котлине, у последнего все входные параметры паблик функций/пропертей д.б. обьявлены как nullable

ps но офенс, но мне кажется, вы не очень хорошо знаете К. Это я не высокомерно, это к тому, что часть вопросов отпадает по мере изучения
Я уже давно понял, как там в котлине. Я пытаюсь объяснить зачем галка, когда мы говорим не про два разных языка, а про две версии одного и того же языка и braking changes между ними.
Но я-то совсем не обсуждаю две версии одного и того же языка. Я обсуждаю котлин
«Просто так», может, и нельзя — но это ж ограничение конкретного SDK для msbuild, а не дотнета вообще.
Если бы это не сделали опциональным флагом, майкрософт бы закидали тухлыми яйцами, а новая версия попала бы в бан во всех компаниях.

Никто не будет переписывать миллиарды строк кода, особенно когда фикс совсем нетривиальный.

К слову, это еще и не ошибкой, а ворнингом будет.
Ну вот с К. это получается. В том смысле, что внутри *.kt у меня все фичи null safety есть; а то, что приходит снаружи, из жавы, помечается как nullable type(не автоматически, увы, но тем не менее)
Это, кстати, обалденная фишка К. — в одном не то что проекте, в одном package могут быть классы как на жаве, так и на котлине
Как вы это будете дружить с библиотеками, написанныйх под 2.0 и которые знать не знают про то, что они должны не нулл возвращать?
Это может стать проблемой, но она решается просто — если метод/класс в котлине принимает параметр снаружи, от жавы, он д.б. обьявлен как nullable type. A дальше уже компайлер не даст его использовать без проверок
Мы котлин обсуждаем, или как в C# 8.0 сделали?

Вам говорят, как в котлине сделать нельзя, потому что в котлине — так, в жаве — так. Как отличать одно от другого в дотнете — непонятно. Вот, сделали флаг. Чем плохо?
То есть вас смущает флаг в котлине? А какой именно?
дискуссия стала непродуктивной, нет смысла ее продолжать
А какие есть альтернативы таскам? Реактор использовать неудобно, а на миллион потоков не хватает системных ресурсов.
Я не про таски, я про треды. Таски хороши как абстракция поверх редов.

А реактор кстати мне вполне понравился, потому что часто бывает задача выбросить таску, которая уже не нужна. В дотнете (и других языках) приходится вводить костыли типа CancellationToken'ов, а с реактором достаточно выкинуть таску и она больше не будет ничего дергать.
Так все-таки, что вы предлагаете для массового использования вместо проактора и асинхронности на async/await?

Да их и предлагаю.


Отсутствие await сильно напрягает.


Например, я затащил в свое время разработчиков C# 5.0 на обсуждение
введения async/await в расте, и там был интересный момент озвучен:


We thought about precedence a lot with 'await' and we tried out many forms before setting on the form we wanted. One of the core things we found was that for us, and the customers (internal and external) that wanted to use this feature, it was rarely the case that people really wanted to 'chain' anything past their async call. In other words, people seemed to strongly gravitate toward 'await' being the most important part of any full-expression, and thus having it be near the top. Note: by 'full expression' i mean things like the expression you get at the top of a expression-statement, or hte expression on the right of a top level assign, or the expression you pass as an 'argument' to something.

The tendency for people to want to 'continue on' with the 'await' inside an expr was rare. We do occasionally see things like (await expr).M(), but those seem less common and less desirable than the amount of people doing await expr.M().

This is also why we didn't go with any 'implicit' form for 'await'. In practice it was something people wanted to think very clearly about, and which they wanted front-and-center in their code so they could pay attention to it. Interestingly enough, even years later, this tendency has remained. i.e. sometimes we regret many years later that something is excessively verbose. Some features are good in that way early on, but once people are comfortable with it, are better suited with something terser. That has not been the case with 'await'. People still seem to really like the heavy-weight nature of that keyword and the precedence we picked.

So far, we've been very happy with the precedence choice for our audience. We might, in the future, make some changes here. But overall there is no strong pressure to do so.
В таком случае я не понимаю причем тут M:N трединг.
M:N трединг это когда средствами языка ну никак не создать отдельный тред. Когда нет ничего уровнем ниже «запустить асинхронную таску». Недостаточно контроля, одним словом.
К сожалению современный Linux объединяет недостатки модели N:1 и M:N.

Вернее ядро-то нормально поддерживает и M:N и N:1, но стандартная библиотека форсирует режим, когда N:1 треды эмулируют M:N. Сигнал отдельному потоку не послать, остановить его тоже нельзя и так далее. Всё в угоду тому, чтобы программы на #%#%^^%*& солярисе с M:N тредами тоже работали…
В таком случае при чем тут корутины без await? Их наличие как-то мешает создавать потоки или что?
Уже и ответили:)
Что лучше в моем хитпараде — это имплементация high-ordered functions
T.e., чтоб обьявить функциональный параметр в сигнатуре другой функции я пишу что-то вроде
fun printFiltered(list: MutableList<String>, filterFunc: (String) -> Boolean)

т.е., не надо использовать Func<> or Action<>, обьявление более натуральное

Что еще лучше — наличие let и прочих. Дотнет сделал гигантский прыжок с ?., но на этом и остановились. Вернее, это все есть в f#, но для него нужен отдельный проект, что не очень удобно

Убрали декларацию типа из foreach, ну и прочее
Это навскидку
Т.е., парни просто пошли чуть дальше
Если говорить о фичах, сокращающий код, то в c# с недавних пор есть
if(int.TryParse("100500", out int i) && i > 200)
 {
//...
}

foreach(var (key, val) in dictionary)
{
}

и всякие другие.
pattern matching, да, отличная штука
а про foreach не знал, спасибо!
про foreach не знал, спасибо!

на самом деле я немного слукавил. Для того, чтоб этот код сработал со словарём, нужно дописать метод-деконструктор. По умолчанию он есть только у кортежей.
public static void Deconstruct<K, V>(this KeyValuePair<K, V> pair, out K key, out V val)
{
        key = pair.Key;
        val = pair.Value;
}

(в C# 7.2 можно out заменить на in, получатся readonly reference)
прикольно, но не очень понятно зачем?
как раз чтоб можно было писать:
foreach(var (key, val) in dictionary)
{
Console.WriteLine($"{key}:{value}");
}
вместо
foreach(var item in dictionary)
{
Console.WriteLine($"{item.Key}:{item.Value}");
}
яркий случай так называемого syntactic sugar:)
как и foreach без декларации типа.
дык. я люблю сахар, но не люблю дополнительных телодвижений. А тут екстеншн, конгресс, немцы какие-то…
UFO just landed and posted this here
Дурацкий вопрос: вы когда-нибудь писали хотя бы одну библиотеку, которой, потом, пользовалась бы 1000 человек? Или хотя бы 10 — но не знакомых с вами?

Поверьте, если бы писали — таких странных вопросов бы не задавали.

И ни разу не натыкался на проблемы из-за того, что что-то излишне открыто. Даже не представляю, как такая проблема может выглядеть.
Очень просто: вы меняете реализацию — и получаете 100500 жалоб на но, что у кого-то отвалился «такой же, но с перламутровыми пуговицами».

Простейший пример — класс как-то обрабатывает массивы и отдельные элементы. Вы перекрыли метод обработки отдельного элемента, а в новой версии обработку массива векторизовали и она функцию обработки отдельного элемента больше не вызывает. Вы будете себя винить или «криворуких разрабов», когда у вас после обновления всё перестанет работать?

Есть же языки, где инкапсуляции в принципе нет — и ничего.
Да, ничего. В смысле — ничего хорошего. Можете поинтересоваться у разработчиков фронтэнда о количестве костылей, которые они наизобретали в попытках запретить-таки людям создавать «такие же, но с перламутровыми пуговицами» сущности. Количество угроханного на это времени и сил исчислению не поддаётся.
UFO just landed and posted this here
final был бы более адекватным в виде аннотации. Всё равно в яве куча способов его обойти, только геморройных.
Ну если вы его явно обходите, то вы должны понимать, что делаете что-то неправильное и ССЗБ.

И всё еще не понимаю прелести передать юзерам моего кода сообщение «обрати внимание, версия поменялась, поведение тоже» через вставление им палок в колеса.
Это не «передача сообщения». Это чёткое отделение вещей, которые они могут использовать, от подробностей реализации, на которые они полагаться не должны.

Посмотрите, скажем, на то, что bionic делает, чтобы через его хендлы никто не залазил во внутренние структуры. А когда-то давно наивные чукотские вьюноши просто отдавали ссылку на внутреннюю структуру… в которую куча разрабочтков немедленно начала лазать своими грязными лапками.

Это как повесить здоровенный замок на хлипкую дверь сарая.
Тут согласен. Но поскольку разработчики Kotlin сараем не владеют, то это лучше, чем ничего. Если не поможет — начнут укреплять стены изнутри (опять-таки смотри bionic)…
UFO just landed and posted this here
А @Deprecated, например, чем менее чёткое отделение?
Причём тут вообще @Deprecated? Как с его помощью запретить перекрывать метод? Не вызывать (это-то как раз нормально), а перекрывать?

Как вам уже написали: если вы не полагаетесь на то, когда и как сам объект вызывает свои методы — то вы можете использовать class delegation. Если же вы перекрываете методы так, что class delegation не работает — ты вы, автоматически, лезете во внутреннюю кухню класса. Что Kotlin, по умолчанию — вам запрещает. И правильно делает.

Да вообще всегда это должен понимать, когда взял другую версию, и не погонял тесты хотя бы.
Ах… не хочу ругаться матрм. То есть для вас нормально, что новая версия библиотеки может что-то сломать?

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

Вообще-то ситуация, когда я беру новую версию библиотеки (о которой автор программы не имеет даже представления), заменяю её на тестовой системе — и через пару дней тестирования запускаю в production — нормальна и правильна. Сбои должны быть исключительно редки. Ситуация, когда вам нужно гонять тесты и что-то править при обновлении библиотеки — это нештатная ситуация! Да, так иногда случается, когда люди не читают документацию (хотя бы знаменитая история с memcpy), но это должно случаться очень-очень редко, буквально раз в год! Это не должно быть нормой!
UFO just landed and posted this here
В прод без тестирования вообще ничего попадать не должно. Ну если у вас хоть сколько-нибудь серьёзное промышленное приложение.
Вы вообще название статьи читали? Я вас уверяю: из миллиона с лишним приложений, которые вы найдёте в Google Play «серьёзных промышленных приложений», которые следуют этому совету — меньше 1%.

А даже для них проблемы при обновлении версий — не должны быть нормой.

Типа такого: @Deprecated("запрещаю перекрывать этот метод")
И будет у вас это предупреждение выдаваться в любой программе, которая будет вызывать этот метод.

Что ничуть не хуже, на мой взгляд, чем: /*не декомпилируй и не инструментируй этот класс позязя*/ final class Foo {}
Не понимаю причём тут декомпиляция и инструментация вообще.
UFO just landed and posted this here
final не пресекает на корню мои поползновения сделать так, как мне надо, ну потому что ява, байткод, матаданные, инструментация.
А ещё JIT. Внезапно, да. Который может даже отлично работать у вас в тестах — но отстрелить вам ногу в проде.

Это низводит его до просто маркера, пожелания, предупреждения, так же как и аннотации типа @Deprecated.
Нет. Если вы проигнорируете @Deprecated — то вы не сломаете программу. Если вы, с помощью рефлекшна, метаданных и прочего обойдёте final — то вы закладываете под ваш бомбу замедленного действия.

Если вы любите «код с запашком», готовый сломаться при изменении параметров запуска JVM — то для ваших услуг есть много других языков. JavaScript, например, или там PHP.

А так, бездумно натыканные final'ы изредка вынуждают меня извращаться с форканием кода либ и последующими мержами; декомпиляцией; инструментацией и тд. Чего ради?
Ради того, чтобы внутренности библиотек оставались внутренностями. И да — это, в свою очередь, требует чтобы API у этих библиотек был достаточно продуман для того, чтобы не хотелось его обойти.

Кто-то якобы будет недоволен тем, что я ломаю совместимость. Ну так обычно я ее не просто так ломаю.
Нет, практика показывает что ломастеры ломают совместимость именно что «просто так». То есть не раз в 5-10 лет, как у нормальных разработчиков, а пару раз в год.

Не нравится — сиди на старой версии.
А вы её будете поддерживать 2-3 года, после выхода новой версии, или сразу бросите?

Как это обеспечить?
Вы не поверите — но тщательно обдумывая API изначально и аккуратно обдумывая все изменения.

Вы ж не можете написать сейчас код и утверждать, что он будет работать в винде с 2000 по 3000.
Не могу. Но это зависит не от меня, а от разработчиков Windows. Которые, в общем, о совместимости думают. Какой-нибудь Microsoft Bob, выпущенный больше 20 лет назад прекрасно работает на последней версии Windows 10. И, думаю, ещё долго будет работать — по крайней мере пока 32-битные версии будут поддерживаться.

Что вам позволит утверждать без вранья, что код, работавший с версией либы 1.0.0.1, будет работать в версии 1.0.0.2, если вы не тестили его с этой версией?
А причём тут я? Если я соблюдал спецификации и делал всё, как описано в доке, то это у разработчиков либы 1.0.0.2 нужно спрашивать — как они собираются обеспечивать совместимость со всем кодом, который работал с либой 1.0.0.1

И если они этого обеспечить не могут — то пусть выпускают версию 2.0.0.0, как и положено, а не делают вид, что это 1.0.0.2
UFO just landed and posted this here
Внезапно что? Каким боком к тому, что я описываю, JIT?
JIT имеет право полагаться на то, что финальный метод будет, внезапно, финальным. И никогда не будет перекрыт. И может, соотвественно, вставить его в то место, где он вызывается. И, более того, то, что он сегодня так не сделал — не означает, что завтра он так не сделает если какие-нибудь счётчики другое число насчитают.

То есть если вы залазите в код и заменяете в финальном классе метод, который, вообще-то, менять не положено — то вы просто напрашиваетесь на то, чтобы получить проблемы.

Если я выпилил из метаданных файналы, откуда jit узнает, что они там были когда-то?
Например если кто-то, не зная о вашей фигурной стрельбе через гениталии, подложит JAR с оригинальным классом, то JIT, внезапно, «узнает» о файналах. А вы о них можете узнать после этого, только когда вас ночью поднимут что-нибудь упавшее срочно чинить.

Я-то не сломаю. Вы сломаете :)
Или другой любитель подкладывать непротестированные либы.
Да, но нет. Если код меняли два человека, один из которых действовал по спецификации, а другой — вопреки ей, то сломал код тот, кто её нарушил. Иногда, правда, чинить приходится не ему: если новая версии библиотек сломает какой-нибудь Facebook или WhatsApp — то для их поддержки могут какой-нибудь костыль забить на некоторое время. А если приложение не столько популярно — то и заморачиваться не будут.

JetBrains вон с идеей jre таскают, явно не от хорошей жизни.
Почему «не от хорошей»? Как раз «от хорошей». Idea отлично работает и с другими JRE, однако им спокойнее, когда они используют ту JRE, что «принесли с собой». Но вот уже libc или там libstdc++ — даже они с собой не таскают.

Не уверен, что они там подхачили, swing вроде.
Ничего не хачили, иначе бы не писали: It's recommended to use the bundled JRE (if available). In case you have any issues with the bundled version, you can switch to the latest Oracle JDK or OpenJDK 1.8 build available on your system.

Целый JetBrains не смог или не захотел спрашивать, похачил сам, а я пойду спрашивать. Хороший план.
Как мы видим JetBrains ничего не хачил, так что я вообще не понимаю о чём вы. Если вы хотите что-то поправить в чужой библиотеке, то ничто не мешает вам её взять и сделать fork (если у вас есть исходники). Но в этом случае это будет уже ваша библиотека — и вы (и никто другой) за неё будете отвечать.

А вот если вы лезете в «кишки» и делаете то, что автор библиотеки не предписывал вам делать — то вы напрашиваетесь на неприятности.
UFO just landed and posted this here
UFO just landed and posted this here

Статья выглядит как крик типичного школьника/студента на тему "это не тот язык который я знаю и люблю, поэтому он плохой, и это никак не связано с тем, что я не смог его выучить".
Практической пользы от неё нет совсем.
Очень жаль, что кто-то может опубликовать такое от имени JUG.ru. Это мелко, низко, написано ради хайпа и не стоит той кармы, которую получил автор.