Pull to refresh

Comments 52

"Скорее всего, будет расти как снежный ком" — пиар, пока о котлине я слышу разговоры только среди разработчиков под андроид. К тому же я не нашел ответа на главный вопрос зачем нужен этот язык, кроме замены java при разработке под андроид и здесь лишь по причине отсутствия собственного толстого SDK.


Те кого устраивает Java — инертные пользователи и крупные корпорации не будут массово переходить, те кто хочет остаться на jvm и получить великолепный язык для быстрого и лаконичного выражения своей мысли используют Scala. К тому же Scala имеет в дополнении к Java экосистеме свою собственную, очень крутую.


Подытоживая, массам разрабатывающим типовые CRUD не нужно ничего кроме Java, профессионалы если хотят уйти от Java, в основном выбирают Scala.

Можно найти и сто причин, по которым Котлин никому не нужен, и сто причин, по которым он всем нужен (например, я лично знаю server-side разработчика, который страшно устал от boilerplate-кода в Java и хотел бы использовать Kotlin из-за этого), и всё это будут предположения и anecdotal evidence.

А можно посмотреть на график в начале текста и увидеть, что по факту он _уже_ растёт как снежный ком — вопрос только в том, до каких масштабов дорастёт.
Я уже кое-что написал как на Kotlin, так и на Scala. Scala слишком академична и непонятна. Kotlin именно что удобен. Нет ничего лишнего, код не похож на набор закорючек и очень много удобных штук по сравнению со Scala.
очень много удобных штук по сравнению со Scala

Вот тут очень бы хотелось увидеть конкретный список.
но список не много дает для понимания, это всё равно что судить о языке по количеству времен и падежей в нем. Только опыт разработки может дать представление, тот ли это язык, на который бы вы променяли скалу, джаву или еще что-то.
Спасибо за список! Правда, насколько я могу судить, согласно этому списку ситуация с «очень много удобных штук по сравнению с...» ровно противоположна той, что заявил автор комментария выше :)
What Kotlin has that Scala does not
  • Smart casts

У Скалы есть Smart casts, только он не через if работает, а в сопоставлении с образцом.


value match {
  case str: String    => str.charAt(0)
  case int: Int       => int + 10
  case Some(int: Int) => int * 10
  case _ | None       => 0
}
UFO just landed and posted this here
в вашем примере он ничего не запоминает

Почему не запоминает? Изначально у value тип Any. В первой ветке идет проверка на то, что value имеет тип String. Если он таковым является, то значение привязывается к str и во всей ветке считается за String. Если бы такого преобразования не было бы, не имелось бы возможным вызвать метод charAt, который есть только у String. Тоже самое касается Int, и Some (аналог Котлиновского null type).

UFO just landed and posted this here

В результате какая разница? Это особенность работы паттерн матчинга. Значения которые по нему сопоставляются привязываются к новому имени. Это спор против мутирования старой переменной или создания новой измененной (считай импер. программирования против функц.). В результате вы так же сделали проверку и у вас произошел неявный каст (не ненужно самому кастовать, как пришлось бы в Java).

UFO just landed and posted this here
очень много удобных штук по сравнению со Scala

Не скажу что прям *много*, но все же они есть:
— В Kotlin хорошо реализован null safely, который встроен в сам язык, что, например, позволяет заменить Option в большинстве случаев на nullable типы, с которыми удобнее работать (тот же?. вместо flatMap, map ..) да и к тому же нет оверхеда на создание лишнего класса.
— Отличный интероп с java. Java из Kotlin выглядит как Kotlin и наоборот. Нет конвертации типов между языками, тот же List в Kotlin это ArrayList в Java и т.д.
— Меньший размер библиотеки (особенно существенно для Android)
— За счет того что язык проще, для него лучше туллинг в студии (не последний фактор также и то что язык от той же студии) и быстрее время компиляции.
— Смарт касты. Ниже в обсуждении Fervus привел пример что в Scala они также есть, это не совсем так, все же они немного разные. В Scala это только работает в pattern matching, а в Kotlin можно проверить неизменяемую переменную на null в if, и внутри ветви с ненулевым значением переменной она будет автоматически приведена к non-nullable типу (тоже самое с проверкой типа переменной). Также smart cast в Kotlin работает в when, где по сути получается урезанный вариант pattern matching из Scala.
— Extension методы, в какой-то мере это аналог implicit из Scala (для задач расширения функциональности существующих классов). В Kotlin также можно передавать extension методы в качестве lambda параметров методов (что весьма удобно, например, для построения DSL)
— функции стандартной библиотеки, всякие apply, run, with, let, also и т.п.
— обратная совместимость, можно всегда жить на свежих релизах не дожидаясь пока все библиотеки переедут на новую версию :)
— достаточно простой результирующий байт код, со всем просто разобраться что во что компилируется.
— inline с «нелокальными» переходами
— делегаты

А так, можно привести комментарий Андрея Бреслава про сравнение Kotlin/Scala из обсуждения: Kotlin, кому что нравится :)
Extension методы, в какой-то мере это аналог implicit из Scala (для задач расширения функциональности существующих классов).

Если extension methods аналог implicit для задачи расширения функциональности, то как можно с помощью Котлиновского extension methods расширить класс внешним интерфейсом?


Отличный интероп с java. Java из Kotlin выглядит как Kotlin и наоборот.

Я задавал этот вопрос в другой теме, но ни кто не ответил. Но все таки повторюсь.
Как сохраняется интероп с java при использовании корутин?
Есть ли возможность вызвать suspend function из java?
Как бы например выглядело использование buildSequence в java?
Ещё по поводу интеропа. Каждый раз слышу заявления про 100% интероп (так же заявлено на сайте Котлина), но в том же докладе Антона Кекс (Kotlin-паззлеры) часто упоминается разного рода косяки при вызове даже java кода и наоборот. Интероп конечно лучше чем у Скалы, но он точно не 100%.


делегаты

А это по правде интересная вещь. Правда, до сих пор не до конца разобрался как это работает.

Если extension methods аналог implicit для задачи расширения функциональности, то как можно с помощью Котлиновского extension methods расширить класс внешним интерфейсом?

C помощью extension методов думаю никак, но можно это сделать делегатом:
class Test: IAddition by Addition() {
    //many code
}

interface IAddition {
    fun add(): Int
}

class Addition: IAddition {
    override fun add(): Int = 100500
}

Как сохраняется интероп с java при использовании корутин?

Корутины сделаны средствами языка, это те же extension методы и т.п.

Есть ли возможность вызвать suspend function из java?

Насколько я знаю — нет, также как и не вызвать inline функции.

Как бы например выглядело использование buildSequence в java?

Вполне себе неплохо.
Код на kotlin:
object Fibonaci {
    @JvmStatic
    fun fibonacciSeq(): Sequence<Int> = buildSequence {
        var a = 0
        var b = 1

        yield(1)

        while (true) {
            yield(a + b)

            val tmp = a + b
            a = b
            b = tmp
        }
    }
}


Вызов на java:
 Sequence<Integer> integerSequence = Fibonaci.fibonacciSeq();



Вполне себе неплохо.

Скорее всего вы меня неправильно поняли. У нас есть функция, под названием buildSequence, которая реализована на языке Котлин, из пакета стандартной библиотеки. Как эту функцию можно вызвать и пользоваться ей в Java коде. При 100% интеропе такая возможность должна быть.
Я попытался вызвать эту функцию из Java и среда разработки предложила мне такой вариант:


Sequence<Integer> seq = SequenceBuilderKt.buildSequence(new Function2<SequenceBuilder<? super Integer>, Continuation<? super Unit>, Object>() {
            public Object invoke(SequenceBuilder<? super Integer> sequenceBuilder, Continuation<? super Unit> continuation) {

                return null;
            }
        });

Корутины сделаны средствами языка, это те же extension методы и т.п.

Не смотря на то, что extension methods это часть языка, их можно вызвать из Java кода явно передав туда объект на котором вызывается метод.


C помощью extension методов думаю никак, но можно это сделать делегатом

Мне не очень понятно где здесь внешние расширение. Вы явно наследуетесь от интерфейса.
Что если у вас есть класс из какой-нибудь библиотеки Java, к которому у вас нет доступа изменять исходники и интерфейс из совершенно другой библиотеки. Можно как-нибудь расширить класс из первой библиотеки другим интерфейсом, например:


// из первой библ
class Robot {} 

// из второй библ
interface Moveable{
  def move(): Unit
}

// мой код с расширением
implicit class ExtMoveableRobot(val self: Robot) extends Moveable{
  def move(){}
}
Я и не говорю что интероп 100%, но тем не менее он очень хороший.

Как эту функцию можно вызвать и пользоваться ей в Java коде

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

Что если у вас есть класс из какой-нибудь библиотеки Java, к которому у вас нет доступа изменять исходники и интерфейс из совершенно другой библиотеки

В таком случае сделать это прям как в Scala нельзя, но можно, например, сделать обертку, а для генерации обертки сделать extension метод на нужном классе (не очень красиво, но и не прям очень страшно будет)
UFO just landed and posted this here
DSL сейчас уже есть, весьма удобный.
Gradle уже сейчас позволяет билд скрипты писать на Kotlin, код почти не отличается от Groovy.

А можете привести пример где нужно именно AST трансформация?
UFO just landed and posted this here
Сущность.выбрать { атрибут1 > 1 }

Такое можно сейчас сделать с extension методами в совокупности в reified generic, если я правильно понял то что вы хотите сделать.
UFO just landed and posted this here
JetBrains на вопрос «нафига?» сказали — это поможет быстрее разрабатывать IDEA.
Интересно — помогло ли?

На днях попробовал. Нужно было сериализовать объект в мап чтобы дальше пустить в бд. Рефлекшен не дает достаточно информации. Да и вообще, зачем решать проблему в рантайме когда вся информация для ее решения есть во время компиляции? Или плодить кучу вонючих орм как в яве? Язык без макросов в 2017-м нельзя называть новым и современным.

Можно подробнее?


Рефлекшен не дает достаточно информации.

Обычно через рефлекшн можно вытащить всё, разве что дженерики стираются, но, как правило, это можно обойти.


Да и вообще, зачем решать проблему в рантайме когда вся информация для ее решения есть во время компиляции?

Оч странная постановка вопроса, вы вроде сами пошли через рефлекшн, а потом спрашиваете зачем?)


В Java стеке есть annotation processing (и в Kotlin он работает), который позволяет для таких задач часть информации вытаскивать в компайл тайм и генерировать код, который, в вашем случае, может сериализовать/десериализовать объект без инспекции объектов в рантайме.


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

Привет Артем, это я, konmik/JackHexen, можно на ты :D

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

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

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

// Епрст, скинь мне уже список своих ников))


Сама либа котлиновского рефлекшена весит под три метра. С фига ли?

Насколько я знаю, они до сих пор тянут тяжелую реализацию protobuf, поэтому так много весит https://youtrack.jetbrains.com/issue/KT-12636 (голосовать здесь)


Все вопросы снимаются, портируемость компайлтайм инспекции кода и кодогенерации на JS и Kotlin Native платформы, имхо, важная часть языка, осталось понять насколько это в планах команды Kotlin, особенно с учетом того, что разработчики на native платформах любят макросы cc abreslav?

Самый-самый толстый protobuf-java весит немного больше 500К, так что я сомневаюсь, что дело в нем :) Наш рефлекшен требует на рантайме большой кусок компилятора, который умеет работать с типовой информацией, вот он и весит так много. Мы будем работать над его уменьшением, конечно, но это потребует какого-то времени.


Все вопросы снимаются, портируемость компайлтайм инспекции кода и кодогенерации на JS и Kotlin Native платформы, имхо, важная часть языка, осталось понять насколько это в планах команды Kotlin, особенно с учетом того, что разработчики на native платформах любят макросы cc abreslav?

Макросов как таковых мы делать не хотим. Нормальный тулинг для статически типизированного языка с макросами написать крайне сложно (какие-то более-менее успешные примеры есть, но их мало, и они весьма непросты по реализации). Наш Scala-плагин к IDEA, например, использует черта в ступе и черную магию для этих целей, причем получает не 100% результат, насколько мне известно. Мы так не хотим :)


Однако это не значит, что у нас не будет никакого метапрограммирования. Во-первых, мы рассматриваем возможность поддержать что-то вроде expression trees, как в C#, но это все-таки рантаймовый механизм. Во-вторых, для compile time мы постепенно будем развивать инфрастурктуру плагинов к компилятору. То есть можно будет написать свой наикрутейший фреймворк, который порождает наихитрейший код во время копиляции, но не на макросах, а в виде compiler plugin. Чем это лучше макросов? Тем, что код, выполняемый в процессе компиляции, не является частью самого компилируемого проекта, поэтому тулинг не должен сойти с ума, чтобы его понять. Плюс, API таких плагинов можно сделать таким, чтобы они одновременно работали и в компиляторе, и в IDE, что, опять же, гарантирует качественную инструментальную поддержку.

Makes sense.


Планируется ли закладывать мультиплатформенность в плагины для компилятора, чтобы плагином можно было генерировать/изменять именно котлин код и не привязываться к конкретной платформе в виде JVM, Native, JS и наоборот, чтобы можно было влиять на компиляцию для конкретной платформы если потребуется?


По поводу API для одновременной поддержки и компилятора и IDE —это мастхев (я так понимаю, напрашивается что-то вроде language-server архитектуры), тк писать плагин для компилятора и потом ещё отдельный для IDE совсем не хочется, но тогда не очень понятно как оно будет жить с разными системами сборки тк от них во многом зависит поведение IDE…


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


Кстати, kotlin-reflect можно попробовать аккуратно пошринкать через ProGuard

Планируется ли закладывать мультиплатформенность в плагины для компилятора, чтобы плагином можно было генерировать/изменять именно котлин код и не привязываться к конкретной платформе в виде JVM, Native, JS и наоборот, чтобы можно было влиять на компиляцию для конкретной платформы если потребуется?

Скорее да, чем нет, но пока это довольно расплывчатые планы.


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

Мы как всегда обязательно всем все расскажем перед тем как непоправимо облажаться :)


Кстати, kotlin-reflect можно попробовать аккуратно пошринкать через ProGuard

Уже :)

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


Compiler plugins for
  • making classes open by default
  • generating no-arg constructors by default
  • extension lambdas in SAM conversions

Есть ощущение что здесь будет такая же проблема, что и в реализациях java-nullability через дополнительные xml — очень легко у разных разработчиков запустить билд с разными параметрами и получить другой результат.

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

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

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

Думаю кейсы подобные вашему могут решиться в будущем с релизом Kotlin Serialization Framework — https://discuss.kotlinlang.org/t/kotlin-serialization/2063


P.S. про макросы и современные языки все же очень спорное утверждение

Макросы решают эту проблему на корню — ты просто пишешь функцию, которая пишет процедуру генерации, и все.

+1
А еще можно и синтаксис расширять, к примеру вот мы получили эти асинки в котлине. Пришлось ждать версии компилятора.

А без расширения синтаксиса этого невозможно сделать?

Генерировать код не так сложно, как можно подумать. Для этого есть очень неплохой инструмент, который работает как для java, так и для kotlin — называется apt (kapt для kotlin-а). Почему это нужно делать обязательно средствами языка?

Аннотейшн процессинг дает очень лимитированную информацию о коде, плюс надо расставлять везде аннотации, и как заметил Nakosika, это не портируется на JS рантайм, и Kotlin Native.

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

> А без расширения синтаксиса этого невозможно сделать?

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

Вообще-то, JVM хорош тем, что синтаксис новый не нужен — достаточно просто поинструментировать байт-код. Собственно, мы в Kotlin так и делаем (аналогично делает какой-нибудь quasar).

Спасибо, интересно.

Котлин ведь позиционируется не только как нашлепка на JVM? Значит ему нужен JVM-независимый способ делать такие вещи. Вот яваскрипт скоро станет многопоточным. Или вебассембли. Просто мысли вслух.

Внутри у любого современного компилятора есть IR, да не один. У Java это — байт-код, у LLVM — биткод или текстовый IR, у gcc — какой-нибудь gimple и т.п. Над IR, в виду его простоты, достаточно тривиально делать подобные трансформации. В Kotlin есть свои IR, но они ещё не устаканились, так что мы пока не публикуем их. В данный момент трансформация в state-машины в реализации корутин для JS и JVM делается по-разному, возможно, когда-нибудь мы сделаем универсальную трансформацию. Основная проблема в том, что на уровне байт-кода (или любого подобного представления) трансформация делается тривиально, а на уровне AST (который удобен для генерации JS) та же трансформация выглядит значительно менее тривиальной, и оставляет в коде значительно больше мусора.


Вообще, наличие простого как пробка байт-кода у Java — это то, за что я так люблю эту платформу. Стоит лишь не полениться его выучить, овладеть такими инструментами как asm, и можно творить такую магию, что никаким lisp не снились (к сожалению, очень мало разработчиков знают о такой киллер-фиче и умеют её правильно готовить). По опыту работы с JS, как с целевой платформой, для него как раз такой фичи очень не хватает. Надеюсь, мы когда-нибудь дойдём до того, что сделаем этакий платформо-независимый kotlin bytecode.

А с расширением синтаксиса не надо приостанавливать выполнение?
Фишка не в том что не надо приостанавливать выполнение (надо — это как раз требование задачи). А в том что макросом это можно сделать независимо от платформы. Написано один раз — запускаешь на JVM, WebAssembly, Native, ETC. А если делать это фишкой компилятора, то придется для каждой платформы по отдельности реализовывать.

И если к примеру ты захочешь сделать свой асинк только с блекджеком, то макросом это можно было бы сделать еще несколько лет назад. А тут народ можно сказать страдал и ел кактус. )))
Макросы да, но без изменения синтаксиса.
Пардон за оффтоп. На упомянутую конференцию Jpoint 2017 есть какие-нибудь промо-коды для хабровчан? :) Цены что-то кусачие, а так бы с удовольствием посетил.
Sign up to leave a comment.