Как стать автором
Обновить

Комментарии 31

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

А вот тулинг (в чём JetBrains мастера), вполне может быть платным и это нормально.
Без бесплатных инструментов язык сейчас не продвинуть, если ты не Apple, конечно :-)
На данный момент всё бесплатно (Kotlin Plugin для IntelliJ IDEA). Компилятор и интеграции к системам сборки, естественно, тоже + Open Source как язык, так и всё вокруг него.
А в чём подвох тогда? Не верится как-то что корпорация делает это просто из любви к программированию, без экономического обоснования :-)
А в чём подвох бесплатности JRE и JDK? В обслуживании и тулзах. Здесь так же, и это прекрасно, для нас — пользователей.

В чём по вашему подвох Swift?
Им тоже надо на чём-то писать новые продукты — тот же CLion — и плагины. Устали ребята от бесконечного boilerplate, общей многословности Java и тупых компиляторов, которые заставляют писать

Dummy dumb = new Dummy();

несмотря на то, что прекрасно знают, какой тип имеет выражение new Dummy(). Как Java лямбдами и всякими totallylazy не обмазывай, всё равно проступают угловатые контуры 90x.
Не раньше, чем плагин Kotlin для Gradle научится перекомпилировать изменившиеся .kt файлы. Шутки-шутками, но я бы даже из своего кармана заплатил, лишь бы на работе можно было перестать беспокоиться насиловать жабу и начать жить писать на нормальном ЯП. Но не волнуйтесь, пока всё слишком сыро.
Пожалуй, на первое смотреть не стоит, потому что метапрограммирование через конкатенацию строк ничем принципиально не лучше сишных макросов. Надеюсь, в D когда-нибудь впилят нормальные AST macros вместо этого костыля, всё-таки 2015 год на дворе, а тот же Scheme это уже 40 лет как умеет (понятно, что в Lisp'ах это реализуется проще, но всё же). Я иногда использую template mixin, но строковый mixin я старательно избегаю, т.к. код генерации строк для mixin обычно выглядит, как синтаксическая блевотина, а попробуешь использовать std.string.format() для генерации строк, так получишь новую проблему — не забыть экранировать %, если генерируемый код использует форматирование или деление по модулю (x % 2). И даже не надейтесь на помощь IDE, т.к. для корректного парсинга сгенерированного кода в IDE нужно засунуть компилятор D. В общем, mixin лучше использовать только в крайнем случае, т.к. отлаживать сгенерённый фарш просто невозможно, а ошибки его компиляции могут посоревноваться в бесполезности с оными для шаблонов C++.
Разумеется по возможности стоит использовать mixin-template. А mixin — в тандеме с token-strings. Конкретно mixin хорош тем, что можно на лету транслировать например HTML-шаблон в код на D и закешировать на диске, а потом просто подключать этот код из файла. При этом всегда есть возможность глянуть что там нагенерилось в нормальном виде (на D), а не в AST. А то, что LISP код — это практически AST — это, конечно, круто, только это явно не Kotlin :-)
Со своей колокольни: никогда не чувствовал потребности в подобных красивостях — там скобочки сэкономить, здесь стрелочку дописать… один черт. Что по-настоящему нужно в Java — это возможность возвращать из функции несколько значений, например как Matlab это разрешает. Вот что было бы здорово.
Про скобочки, пожалуй, правда ваша, в тех разумных пределах, пока синтаксический шум не мешает читать код.
А насчёт возврата нескольких значений — Kotlin так умеет:
val (x, y, z) = points3d[0]
или даже
for ((k, v) in hashMap) { /* ... */ }
Настоящих tuples, правда, нет (мотивация — не нужны), и примеры выше работают за счёт реализации методов component1..N.
>> А насчёт возврата нескольких значений — Kotlin так умеет:

Насколько я понял, все равно придется определять класс (да еще с определенными методами) и запихивать возвращаемые значения в него. Пример use case описал ниже, именно от этой работы хочется избавиться.
Придётся, но, как в ветке ниже верно отметили, эта работа сведётся к одной строчке кода прямо перед объявлением метода. Для класса, помеченного аннотацией data, компилятор сам сгенерирует equals, hashCode, copy и component1..N. Ну, ещё есть Pair и Triple в стандартной библиотеке, если семантику полей не жалко. :)
В отличие от многих других синтаксических проблем Java, эта решается относительно легко при помощи коллекций или специальных классов. Даже если бы в Java были кортежи, я бы всё равно предпочёл использовать классы, т.к. кортежи привязывают тебя к определённому порядку значений и никак не спасают от путаницы, если хотя бы два значения в кортеже — одного типа. Короче говоря, уровень самодокументируемости у них почти равен нулю. Классы, напротив, завязаны на имена, поэтому совершенно не важно, какое поле у возвращаемого объекта вы хотите прочитать первым, и при обращении к одному из них вы точно будете знать, что получаете.

Посудите сами, зачем бы вам были нужны кортежи, будь в Java короткий и ясный синтаксис Kotlin:

В случае scala в простых случаях достаточно кортежей (а они там до 42-арных), а во всех остальных удобно использовать case class'ы, которые являются immutable, имеют простой синтаксис использования, удобное создание и декомпозицию (для pattern matching), реализуют разумные equals/hashCode/toString и т. д.
а они там до 42-арных

А вдруг не хватит?
Это скорее к java 8, где решили, что Function/Consumer/Predicate и BiFunction/BiConsumer/BiPredicate хватит всем.
есть пакет tuple или можно обычные коллекции возвращать
Речь о самом языке, дополнительные библиотеки не всегда хочется тащить. А возвращать коллекции типа Object тоже дурная работа.
тогда заворачивайте в Data-класс результат
Ой, рука случайно дёрнулась и отправила комментарий раньше времени.
Так вот:

data class Point2D(val x: Int, val y: Int)

Если у вас синтаксис лаконичен, как в Kotlin, вам не составит труда написать ещё один data-класс в одну строчку, придумать для него осмысленное название и лишний раз поразмыслить, а действительно ли вам нужно возвращать несколько значений и законно ли это вообще — иметь у функции несколько выходных параметров, замаскированных под возвращаемое значение? Что такое вы собираетесь оттуда возвращать, что вам нужно делить это на части? Если возвращаемы значения независимы, не слишком ли много обязанностей на себя берёт функция? Если же они связаны, то ведь классы для этого и предназначены: связывать данные в отдельную монолитную сущность — как, например, Point2D. Кортежи позволяют делать такое:

y, x = getCursorPosition(); // упс, баг

Баг здесь появился только потому, что кто-то решил, что x и y независимы, но это не так: они часть единой сущности — точки в двумерном пространстве, и не имеют смысла по отдельности. В следующем коде так же не нужны кортежи:

file, err := os.Open("file.go")

Goты утверждают, что это якобы заставляет больше думать об обработке ошибок. Они серьёзно? Первым же делом после предъявления этого дутого аргумента они сами показывают, как доказать его несостоятельность:

file, _ := os.Open("file.go") // обработай это, чудила! ghetto style

А выкидывай они исключения вместо кодов ошибок, и не думать об обработке ошибок уже бы не вышло, при этом бизнес-логика была бы отделена от обработки ошибок, и можно было бы обрабатывать ошибки на выбранном уровне абстракции, а не делать это в каждой функции. В общем, спорная фича эти кортежи и прочие multiple return values.
>>>>а действительно ли вам нужно возвращать несколько значений и законно ли это вообще — иметь у функции несколько выходных параметров, замаскированных под возвращаемое значение? Что такое вы собираетесь оттуда возвращать, что вам нужно делить это на части? Если возвращаемы значения независимы, не слишком ли много обязанностей на себя берёт функция?
<<<<

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

Теоретически можно придумать какой-то класс, объединить их в структуру и т.д. Но зачем? Ради одной-единственной передачи из функции? А если подобных функций у вас десятки? Что для кода с некоторой математикой/алгоритмикой скорее правило, чем исключение. Matlab без этой фичи вовсе не выжил бы, а в Яве приходится какими-нибудь Object[]{, } извращаться.
В Java, пожалуй, это имело бы смысл. Но в Kotlin это всего одна лишняя строка:

data class Region(val entrance: Node, val exit: Node) // вот эта
fun Graph.findSomeRegion(): Region = ...
...
val region = graph.findSomeRegion()

и вам не надо помнить, что идёт первым — entrance или exit, даже если лень писать тесты. В этом конкретном случае очевидно, что entrance должен идти первым, но только для нас с вами, а вот компилятор скромно промолчит, если вы их перепутаете, т.к. оба значения имеют одинаковый тип.

Впрочем, возможно, что я перегибаю палку. Просто обычно я стараюсь переложить максимум работы на компилятор, потому что даже со 100% покрытием кода тестами нельзя гарантировать отсутствие багов, и лучше баги находить до того, как их найдут клиенты. Но находить баги при сборке для меня недостаточно, я стараюсь по возможности писать код так, чтобы исключать целые классы багов ещё до сборки (используя Option вместо null, например). Грань между программерской ленью и перфекционизмом довольно тонка :)
А если используется только один из них, а второй можно было бы и не вычислять? Как реализовать ленивое вычисление значений только если оно действительно нужно? Что делать, когда кроме входного узла и выходного нужно будет возвращать ещё пачку дополнительных данных? Пихать всё это в огромный кортеж? Подход яваскрипта тут лучше смотрится:

function findSomeRegion() {
    ...
    return { entrance : nodeEn , exit : nodeEx }
}
...
var region = findSomeRegion() // get all data as object
var { nodeEn , nodeEx } = findSomeRegion() // destruct object to variables
Кстати, о лени: totallylazy. Имеются Sequence + генераторы, Option, а также filter, map/flatMap, fold/reduce и ещё кучка сверху. Без этой библиотеки и Retrolambda я бы уже повесился.
У Kotlin, насколько мне удалось понять из документации, есть один очень серьезный дизайновый изъян (тот же, что и в C#). Там в архитектуру не заложены исключения, которые «трудно проигнорировать». Я говорю, разумеется, про checked exceptions. При этом объяснение в духе «глупые обезьяны всё равно выкрутятся, написав catch (Exception e) { }» совершенно несостоятельно. Мне нравится, что компилятор не дает мне «прозевать» IOException. А если совсем «приспичит», я этот IOException заверну в RuntimeException. Но 9 из 10 случаев, когда исключение не ловится — это когда оно тупо «прозёвывается».

Именно эта строгость Java и делает ее почти единственным приемлимым языком в гигантских Enterprise-проектах, как мне кажется.

Ой… нечаянно написал ответ.
С проверяемыми исключениями далеко не всё однозначно, и в Java такой подход был скорее экспериментальным. В документации по поводу отсутствия проверяемых исключений в Kotlin ссылаются cюда, сюда и сюда.
Да-да. «один умный дядя сказал». Я читал эти статьи.

У меня критерий проверки — собственная шкура. И аргумент простой. Если кто-то говорит «это — ненужная фича — выпилить», а мне она помогает, я шлю этого «эксперта» вдаль, несмотря на его авторитет. Потому что эксперимент для меня ценнее любой теории.

К сожалению, запретить «проглатывать» исключения нельзя. Но то, что можно (и нужно) писать код, где каждый Exception отлавливается всегда, когда есть опасность, что он случится (хотя бы минимальная), и что я сам — ленивая сволочь, которая не напишет обработчик, если ее не пнуть, и что моё собственное честолюбие не позволит мне это исключение «проглотить», означает, что лично я с checked exceptions пишу более качественный код. И никто в обратном меня не убедит. А еще я верю, что я не один такой.

Я перед Java пару лет посвятил C#. Язык в большинстве возможностей мощнее и лучше, чем Java, но когда я увидел checked exceptions, то тут же пожалел, что там их не было. Кучу проблем в моем старом коде это бы решило еще на этапе его набора.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации