В ноябре стало известно, что работу над Kotlin возглавит Роман Елизаров (elizarov). Поскольку теперь за дизайн языка отвечает он, интересно лучше понять его видение. Поэтому на онлайн-конференции Joker мы задали Роману много вопросов. А теперь для Хабра сделали текстовую версию (видеозапись тоже прикрепляем).
Для начала поговорили о его новой роли и команде Kotlin, а затем перешли к дизайну языка. Из текста можно узнать, например, такие вещи:
- Как принимаются решения, ломающие обратную совместимость?
- Чем философия Kotlin отличается от C# и почему?
- Насколько приоритетна производительность?
- Чем корутины в Kotlin отличаются от Project Loom в Java?
- Какой тренд влияет на языки прямо сейчас?
Расспрашивали Антон Архипов (Developer Advocate в JetBrains) и Алексей Стукалов (руководитель направления DevRel в компании Haulmont, создавшей CUBA Platform). Но не все вопросы они придумали сами: поскольку дело происходило на конференции, часть была написана зрителями.
Взаимодействие Project Lead, команды Kotlin и партнеров
— В чем заключается позиция Project Lead? Это бог и папа всего проекта или какая-то номинальная должность?
— Номинальная. Я — точка входа для JetBrains в Kotlin: разруливаю, куда, что и кому отправить, помогаю всем находить какие-то варианты, представляю Kotlin в каких-то организациях (и в случае с партнёрами, и внутри JetBrains). И название Project Lead неслучайно: это не Project Manager, мне не надо «управлять» кем-то.
Я отвечаю за язык, и формально на мне финальное решение, пойдет ли в него та или иная фича. Но Андрей Бреслав за 10 лет построил сильную команду, куча инженеров разбирается во многих фичах на порядок лучше меня, они делают вещи, предлагают, обсуждают. Поэтому моя цель больше формальная. Нет и близко никакого диктата, как это было в Python.
И Kotlin — очень открытый к сообществу проект. Мы не Swift, где Apple может в закрытую что-то пилить, а потом бац — выкатить и поставить комьюнити перед фактом. У нас секретов нет, все эксперименты видны на GitHub.
— Звучит как плоская структура, а можешь подробнее рассказать, из кого состоит команда?
— Kotlin очень большой, у нас больше 100 человек, и они сформированы в небольшие команды, каждая занимается своим кусочком. Этот «кусочек» может быть технической частью (например, JVM-бэкенд), а может — продуктовым направлением (например, Kotlin Multiplatform Mobile).
Есть целых несколько команд компиляторов, потому что у нас есть и Kotlin/JVM, и Kotlin/JS, и Kotlin/Native — разные бэкенды. Есть общий фронтенд, которым занимается другая команда. Есть какие-то инфраструктурные проекты, IDE, библиотеки, поддержка, тестировщики, маркетинг, техписатели. Небольшая команда технических писателей обслуживает весь проект — она отдельная, потому что нужны люди определенной квалификации, которым проще быть в одной команде, и они делают всё для всех.
— Все эти команды независимы друг от друга, у них свой релиз-цикл и своё понимание дальнейшего развития?
— В целом, конечно, нет. Основное событие Kotlin — релиз компилятора, который мы сейчас делаем 2 раза в год, плюс IDE к нему. Это большие релизы, ещё есть патчи и другое.
К этим релизам привязана работа почти каждой команды: техническим писателям надо написать тексты, саппорт должен быть в курсе, PM должны поработать с пользователями, адвокаты должны донести мысль и так далее.
И даже библиотеки, у которых мог бы быть отдельный цикл, привязаны. Потому что в языке появляются новые фишки, а значит, нужна библиотечная поддержка, это должно быть синхронизировано.
Есть общие направления, куда мы движемся. О них знает каждая команда, и ребята понимают, какой вклад в эти направления они могут сделать, чтобы попасть в это светлое будущее.
— Синхронизация такого количества команд и людей — это отдельная задача. Это зона ответственности Project Lead, или оно само собой работает?
— Само собой, конечно, ничего не работает. У нас есть несколько человек, которые занимаются этим. Есть Стас [Ерохин], который занимается всей разработкой, есть Егор [Толстой], который координирует все технические команды. Есть продакт-менеджеры, которые общаются с командами по своим продуктовым направлениям.
И много работы происходит в разных плоскостях. Тимлид маленькой команды часто сталкивается с задачами, которые требуют участия не только его команды. Все хорошо друг друга знают, обсуждают и решают общие задачи.
— Было упомянуто взаимодействие с компаниями-партнёрами. Google — большой участник экосистемы Kotlin. Как вы с ними взаимодействуете?
Роман: Google — действительно крупнейший партнер, и неспроста. Они не просто поддерживают Kotlin так, как это делают другие компании. Есть анекдот про свинью и курицу, где курица говорит: «Давай мы откроем ресторан под названием “Яйца и Бекон”». Свинья подумала: «Не, так не пойдет, I'll be commited, but you'll just be participating».
Курица и свинья идут по дороге.
— Слушай, Свин, я тут подумала, не открыть ли нам с тобой ресторан? — говорит курица.
— А какое название дадим? — спрашивает свинья.
— Как тебе «Яичница с беконом»?
— Не пойдет — мне тогда придется посвятить себя проекту полностью, а ты будешь задействована лишь частично!
«Scrum. Революционный метод управления проектами», Джефф Сазерленд
То же самое и здесь: тех, кто просто участвует, очень много, но Google реально «committed», и это делает компанию уникальным партнером для нас.
Но влияние Google всё же ограничено влиянием. Понятно, что их проблемы получают приоритет, но, например, весь дизайн языка и общее направление в наших руках. Это мы разрабатываем Kotlin, а они — один из контрибьюторов. У них есть инженеры, которые фиксят компилятор и присылают пулл-реквесты с какими-то конкретными багфиксами, которые специфичны для Android. Но это один из контрибьюторов, пусть и самый крупный. Kotlin в любом случае — продукт JetBrains.
— В каком-то плане они от вас зависят, потому что они должны быть встроены в ваш релиз-цикл. У них есть Early Access?
— Мы координируемся с крупными партнерами так же, как и внутри между собой, когда все команды понимают, какие у нас общие планы и цели. Это не только Google, но и Gradle, Spring.
Образование и спортивное программирование
— Бытует мнение, что «котлинист» — это следующий шаг «джависта». У новичков возникает вопрос: тогда стоит начинать с Java или можно сразу постигать Kotlin? Как вообще строится Kotlin для новичков, как вы завоевываете людей с чистого листа?
Роман: Ответ очень простой, у меня на эту тему есть блог Intentional qualities в Medium.
Когда ты создаёшь продукт, какое-то его качество не может получиться случайно. Если ты что-то создаёшь и не ставишь целью, чтобы оно было быстрым — оно не станет быстрым. Это очень важное наблюдение применимо и к удобству для образования.
Создатели Java не подумали ни секунды о том, будет ли удобно учить язык Java в начальных классах образования, такой задачи не стояло. Были другие проблемы, решались другие задачи. И получилось что получилось.
Как новичку объяснить, что такое public static void main
? Ответ — никак.
— Я скажу «просто не трогай, так должно быть».
— Да! А Kotlin изначально задумывался как понятный и простой язык, который можно понять и которому легко научить. Поэтому учить его с нуля гораздо проще, чем Java. И мы активно работаем с вузами, у нас есть специальный человек, который только и занимается Kotlin в образовании. Очень рекомендую посмотреть ролик, который выпустили к нашему онлайн-ивенту. Там преподаватели, уже использующие Kotlin в образовании, рассказали, почему выбрали его.
Мы получили много фидбэка, который подтверждает, что если пытаешься сделать что-то полезное — это получает отклик людей. Они видят, что новичкам проще на Kotlin, чем на Java.
Поэтому ответ прост: начинайте учить Kotlin, а когда станете сильным программистом, вы наберете в багаж ещё много языков. Сильные программисты точно знают десяток языков и ещё три имеют в активном репертуаре. Поэтому постепенно всё изучите.
— К движению олимпиадного программирования, ICPC, относятся по-разному: одни считают такое пустой тратой времени, другие бесценным опытом. Два вопроса: когда Kotlin войдёт в число языков, на которых там можно писать, и как ты сам относишься к этому времяпрепровождению?
— Я вижу, что ты явно отстал от спортивного программирования, потому что там давно можно писать на Kotlin. Более того, JetBrains — один из спонсоров ICPC, потому что занимается инструментами для программистов и предоставляет инструменты для всех участников соревнования.
Что касается вопроса про времяпрепровождение — всё очень индивидуально. Люди разные, их мотивации развивать свои способности и учиться тоже. И есть категория людей, которые хорошо мотивируются соревновательным моментом. У меня, например, дочка такая с детства: ей интересно, когда есть челлендж, когда ты работаешь не против абстракции или самого себя, а есть с кем помериться силами.
И благодаря ICPC появились многие IT-специалисты, которые вместо того, чтобы гулять и прожигать жизнь, день и ночь изучали алгоритмы, тренировались программировать — и получались такой силы программисты, которых нигде не сыщешь.
У нас в команде много ICPC-шников, точное число не назову, но лично знаю четырех, все — очень сильные инженеры. Я знаю, что если человек с ICPC — это инженер, который может разобраться в проблеме и которому можно поручить любую неподъемную задачу. Потому что ICPC мотивирует людей прокачивать мозг. Даже если им конкретно эти олимпиадные алгоритмы потом не пригодятся, мозг всё равно прокачался.
Дизайн языка
— Перейдем к дискуссии о дизайне Kotlin. Можем начать с достаточно философской темы: откуда берется понимание, в какую сторону вообще развивать язык?
— Это действительно философская тема. Начнём с того, что у нас вся команда программирует на Kotlin: чтобы развивать язык, надо на нём программировать, это очень важно. Каждый, кто участвует в дизайне языка, постоянно его использует. И это первый важный источник: мы сталкиваемся с этим языком, видим, что нам нравится, а где есть шероховатости, и что можно улучшить и исправить.
Но мы — это мы. Хоть нас и 100 человек, но мы пишем только библиотеки, компилятор, тулинг, IDE. Это маленькая доля того, что на Kotlin пишет наше огромное сообщество. Они пишут вагоны разных приложений, других библиотек, работают в сложных доменах, которые нам и не снились. И они нам приносят проблемы, каждый видит их в своём домене. Например, «тут на бэкенде не очень удобно, приходится писать boilerplate-код, хотелось бы упростить».
При этом Kotlin — универсальный язык, а не специфическое решение для решения задач в узком домене. Он должен приносить пользу широкому кругу программистов. Поэтому наша задача — переработать этот материал, осознать его, провести через собственный опыт, и найти какой-то обобщающий подход. Это сложная и творческая задача: сгруппировать, обобщить и понять, какие проблемы — универсальные.
Есть, конечно, неравенство: запросы, которые идут из JetBrains — первоочередные. Потому что, извините, мы делаем язык, в первую очередь надо, чтобы нам было хорошо. Во вторую очередь — наши большие партнеры (но это не означает, что приходит Google, говорит: «Мы написали библиотеку с такой-то фичей», и мы непременно побежали её имплементировать). Затем комьюнити. Но все интересы учитываются. Есть приоритизация, а дальше идут сложные трейдоффы.
Опять же, всё это не значит, что я один сижу и решаю. Порой бывает так, что инженер-программист сказал: «Мне это надоело, пойду-ка я заимплеменчу в свои 20% свободного времени», когда он может делать, что хочет. Приносит уже готовую реализацию, мы смотрим — о, отлично, работает, вперёд. Почему бы и нет.
— Посматривает ли команда Kotlin в другие новые языки — есть ли там что-то полезное, что стоит внести в код?
— Действительно, Kotlin изначально родился как синергия трендов и тенденций современных языков программирования. У Андрея есть замечательный доклад про то, какие языки повлияли на Kotlin, там он подробно идет по фичам и рассказывает, что для них послужило вдохновением. В докладе видно, что есть и C#, и Groovy, и Java, естественно — и это влияние никуда не делось.
Очень многие говорят: «Kotlin очень похож на Swift, кто на кого повлиял?» Интересно, что никто ни на кого. Apple разрабатывал Swift закрыто. Kotlin разрабатывался совершенно независимо, JetBrains работала над ним закрыто меньше года, а затем проект был анонсирован и работа пошла открыто. Как мы теперь знаем, Swift тогда был на финальных стадиях дизайна. То есть Kotlin не мог повлиять на Swift, потому что там уже долго работали внутри и почти финализировали дизайн.
И получилось, что они очень похожи. И это неспроста. Во всём сообщество программистов есть общее ощущение, что такое современный язык, как он должен выглядеть, какие фичи в нем должны быть. Оно объективное, потому что программисты сталкиваются с похожими проблемами, и понятны какие-то общие пути их решения.
Современные языки похожи, что неудивительно. Идет постоянный обмен фичами, знаниями и опытом между разными языками. У кого-то что-то поработало хорошо — тут же другие языки начнут затягивать похожую функциональность.
— То есть протекание фич из языка в язык — это натуральная вещь, она происходит просто потому, что кому-то что-то нравится и надо реализовать в другом что-то похожее?
— Даже не потому, что нравится, а потому что современное прикладное программирование, например, «мы пишем бизнес-софт и решаем бизнес-задачи» — очень похоже. Не важно, на каком языке ты пишешь: на C#, Java или Kotlin — у тебя те же самые проблемы, ты используешь похожие шаблоны проектирования, ты сталкиваешься с похожими проблемами, которые надо выразить в коде.
Ты не хочешь вагоны boilerplate, чтобы создать бизнес-решение. Ты хочешь, чтобы оно было компактное, чтобы код отражал твою бизнес-мысль. И многие языки (не только Kotlin) стремятся снизить этот барьер, нащупать какие-то решения, которые позволяют снижать барьер между мыслью и кодом.
И если это решение было хорошо нащупано, то его быстро одобрят. Например, C# придумал async/await и запустил его в массы в 2010 году. Но проблема была у всех! Увидели крутое решение — раз, и оно всюду, потому что проблема всеобщая, и её нужно решить. И это было огромным вдохновением также и для Kotlin, чтобы сделать корутины. Любой современный программист сталкивается с асинхронщиной, без неё никак в современном мире.
— Это может означать, что существует какой-то асимптотически самый крутой язык, к которому все стремятся, и лет через 20 все языки сольются в него?
— Нет-нет! У каждого большого языка со множеством пользователей есть уникальная философия, и за каждым стоит какая-то единая мысль. Хорошие языки не затаскивают фичу просто так.
Я рассказывал, что кто-то в команде может прислать пулл-реквест, но это не значит, что любой пулл-реквест будет принят в Kotlin. Он в первую очередь должен соответствовать идеологии языка, общему направлению. Просто шанс, что в команде Kotlin кто-то сделает такое, существенно выше, потому что люди там больше прониклись этой культурой, они понимают, что такое Kotlin, больше, чем человек снаружи.
У каждого большого языка есть такая философия, и нет такого, что языки слепо затягивают фичи. Они всегда перерабатываются через призму философии этого языка.
— Попробуем понять философию Kotlin на примере. В C# есть ключевое слово event. Оно не несёт ничего сверхъестественного, всё достаточно легко реализуется в функциональном стиле. Для «шарпистов» оно удобное и они спрашивают, как мы без ивентов вообще живём.
Вопрос: появятся ли в Kotlin ивенты? И какая часть философии запрещает или заставляет завести нечто подобное?
— Этот пример как раз хорошо иллюстрирует отличия философии дизайна C# и Kotlin. Обычный процесс создания фич в C# такой: есть какая-то проблема, и мы её решаем. Затаскиваем какую-то новую фичу, специально делаем ключевые слова. Были у людей проблема с генераторами — сделали генераторы с yield и так далее. Есть проблема с асинхронностью — затащили ещё два ключевых слова async/await. Надо добавить ивенты — сделали специальную фичу event. Надо конструировать объекты, пересоздавать и подменять в них какие-то поля — сделали специальную конструкцию with. И это очень шарповый подход: создавать уникальную синтаксическую конструкцию для решения конкретной проблемы.
И в целом у C# тенденция целенаправленно «прибивать фичи гвоздями». Ну, иногда бывает процесс обобщения, как это было с async/await, когда сначала прибили всё к конкретному типу task, а затем обобщили.
И это хорошо для Microsoft, которая строит свой мир по принципу «я всё контролирую сверху вниз. Я бог, и я знаю, что вы будете делать. Я делаю не просто язык, а язык, с помощью которого вы будете писать на наших библиотеках, под наши application-сервера, деплоить в наше облако и редактировать в наших IDE».
Для Microsoft естественна такая философия дизайна. Когда они делали async/await, они специально делали его под конкретную библиотеку с конкретными задачами.
В Kotlin другой подход к дизайну. Мы ничего не контролируем, мы пишем язык, который будет использован в миллионах разных доменов. Мы не можем заточить и не имеем права заточить его под конкретную узкую специфику — свою библиотеку, своё решение. Во-первых, потому что их у нас просто нет, во-вторых, цель не в этом.
Когда мы сталкиваемся с проблемой, мы ищем способы её обобщить, стараемся как можно больше задач вывести в библиотеку и минимум — в язык. Мы стараемся минимизировать языковую нагрузку, но максимально увеличить гибкость, чтобы потом могли прийти разработчики библиотек, решив свои проблемы в своём домене доменно-специфичным образом и воспользовавшись той или иной более общей фичей языка.
Поэтому у нас нет ивентов, но есть перегрузка оператора +=, есть лямбды. Ты можешь легко и непринужденно набросать в 10 строк DSL, который для конечного программиста будет выглядеть ровно как ивенты в C#.
Язык даёт тебе «строительные блоки», из которых ты в конечном счете соберешь штуку, которая тебе нужна в конкретном домене. А в другом домене нужно другое, значит, из строительных блоков можно собрать что-то другое.
То же самое с корутинами, нет прибитых ключевых слов async и await. У нас библиотечные примитивы, ты можешь написать библиотеку, в которой они делают другие вещи. В языке мы делаем минимальную поддержку, чтобы на библиотеках можно было дальше всё достроить.
И это повсюду. Посмотрите на любую фичу Kotlin — она будет именно такой. И это очень хороший пример отличия философии дизайна. И какие два языка не сравни, найдешь отличия. У каждого свой подход.
— Порой джависты говорят про Kotlin: «Зачем столько способов написать одно и то же?» и заявляют, что язык перенасыщен разными языковыми структурами, из-за этого возникают достаточно серьезные сложности во время код-ревью. Писать приятно — ревьюить сложно. Есть ли у вас какой-то гайдлайн: «здесь мы больше не добавляем синтаксических структур, потому что идёт перенасыщение?»
— Гайдлайна нет. Но дизайн языка — это вообще минное поле компромиссов. Любая фича в языке очень дорого стоит. По дефолту на любое предложение добавить что-то в язык — ответ «нет, не надо ничего добавлять».
Чтобы фича была добавлена в язык, она должна не просто быть полезной, она должна быть дико полезной. Преодолеть очень высокую планку, чтобы перевесить все недостатки хотя бы самого факта наличия ещё одной фичи в языке.
Естественно, здесь компромисс, в котором нет правильного ответа. Разные языки находят себя в разных точках. Есть сознательно очень простые языки, такие как Go, которые выбрали подход «мы будем максимально простыми».
Но из-за этого возникают проблемы экспрессивности, когда ты не можешь написать даже простые вещи, потому что начинаются проблемы абстракций. Вместо того, чтобы абстрагироваться, я должен заниматься копипастой и написать codegen.
А на другом конце очень экспрессивные языки, тот же Lisp, на котором только библиотек, которые делают одну вещь, может быть десяток, и они между собой не дружат. Можно сделать экспрессивный язык, на котором что угодно соберешь библиотекой. А истина где-то посередине, и это очень сложный поиск компромисса.
Мы всегда об этом помним, у нас всегда масса примеров перед глазами. Мы лавируем, поглядывая по сторонам. Мы знаем, что если сейчас будем упрощать и ничего не делать, то превратимся в Go. Мы туда не хотим, потому что видим большие проекты на Go, где приходится делать codegen и так далее.
Так нельзя. Сила программирования — в абстракции, когда я могу какие-то повторяющиеся штуки абстрагировать в библиотеку и переиспользовать. Это то, что любят программисты.
С другой стороны, я могу дать пользователю свободу в этой абстракции, заводить макросы и так далее. И увидеть, что это создает другие проблемы. Когда я даю пользователю возможность писать произвольные макросы, всё становится так клёво с точки зрения абстрагирования, что весь тулинг начинает разваливаться. IDE не может понять, как рефакторить, в коде становится вообще ничего не понятно.
Поэтому у нас есть не то что бы понимание «как надо», скорее, понимание «куда мы не хотим». У нас постоянные страшилки на дизайн-митингах — «ой, не надо, а то превратимся в Scala» (или ещё во что-то). Это для нас является ограничителем: так уже пробовали другие, и мы знаем, что вышло.
— Интересно, что для вас важны возможности тулинга. Учитывая, что тулинг делаете тоже вы, для вас очень важно, чтобы та или иная фича в языке была поддержана и поддержана хорошо. Правильно?
— Нет, тут причинно-следственная связь несколько нарушена. Дело не в том, что мы делаем тулинг. Дело в том, что Kotlin изначально задумывался как язык индустриального программирования, на котором будут писать большой промышленный продакшн, прикладной код с десятками миллионов строчек. Он явно был задуман для этого, не для написания скриптов.
Если в Web Archive поискать старые документы про Python, найдётся вступление, что Python подходит не только для создания маленьких программ, но и для больших: больше 10 тысяч строк кода. Тогда для Python программа больше 10 тысяч строк казалась дико большой.
Когда создавался Kotlin, изначально была задача, что мы на нем будет писать программы от 1 миллиона строк кода. А из этого автоматом следует тулинг: как поддерживать программу в миллион строк без него? Из этого следствие — язык должен быть изначально продуман так, чтобы он был понятен инструментам.
А то, что мы пишем эти инструменты, это так завелось. Благодаря этому у нас есть опыт, и до сих пор в дизайн-коде у нас участвуют люди с опытом в тулинге. Это показывает границы. У нас опыт поддержки больше 10 языков, и мы знаем, что вот эту фичу делать не надо, потому что мы потом умрём поддерживать её в тулинге.
— Чем Kotlin принципиально отличается от Java? Или Kotlin — красивая упаковка для Java, как Swift для Objective-C?
— Swift и Objective-C — совершенно разные языки. Хотя они интероперируемы между собой, и дизайн Swift заточен на то, чтобы хорошо оперировать с Objective-C.
То же и здесь. Kotlin — это новый язык, который изначально был задуман, чтобы легко дружить с Java. Но это совсем другой язык, у него другая идеология. Он создавался для большого промышленного программирования, которым приятно пользоваться и работать в масштабе.
Изначально стояла задача решить боли Java. Огромное влияние на дизайн Kotlin возымела книга «Effective Java». Там советы — что в Java надо делать, а что не надо. И если вы её прочитаете, обнаружите, что половина этих вещей уже учтена в дизайне Kotlin. Если там написано «не делайте этого», то в Kotlin этого просто нельзя сделать. Если там написано «всегда делайте вот так», то в Kotlin просто сделана такая фича из коробки.
И Kotlin изначально задумывался как язык, учитывающий недостатки Java: он исправляет её проблемы с системой типов, добавляет null safety (потому что известная большая проблема у любого Java-проекта — это NullPointerException), решает проблемы с многословностью (потому что тебе постоянно нужно делать codegen или писать геттеры и сеттеры), решает проблемы с абстракциями (потому что в Java по своему опыту сталкивался с паттерном, что каждый вызов нужно заворачивать в try из пяти строчек кода, определенным способом написанный копипастой).
В Kotlin ничего не надо заворачивать — один раз написал библиотеки и используй их как встроенные конструкции языка. И таких вещей в Kotlin десятки. Много проблем, которые в Java доставляют неудовольствие и заставляют писать повторяющийся код, провоцирующий делать больше ошибок, в Kotlin просто устранены.
— О Kotlin вечно говорят в паре с Java, но сейчас он запустил корни уже в самые разные аспекты разработки: и фронт, и натив, и мобильная разработка. Остаётся ли JVM-платформа главным таргетом, или все одинаково полезны? И если что-то нельзя сделать на всех платформах, вы откажетесь это сделать?
— Будем честны с собой. Да, сейчас для Kotlin JVM — это основной рынок, и JVM — самая большая платформа в мире для разработки на бэкенде. Но если посмотреть на графики, то эта платформа в целом не растёт. (Хотя и не умирает — крики, что завтра Java умрёт, преувеличены.)
Новая интересная движуха происходит за пределами JVM-платформы. Появляются новые языки и технологии, фронт, cloud. То есть рост IT-рынка происходит за пределами JVM. А Kotlin задумывался не как сиюминутный язык, чтобы поиграться и бросить — это игра вдолгую. Мы планируем работать над Kotlin многие десятки лет и оставаться современным языком.
Это и драйвит развиваться на другие платформы, в растущие направления. Растёт облако — идём в облако, интересен фронт — идём во фронт. Мы развиваем те точки роста, где в будущем будут программисты.
Когда мы начинали Kotlin, приложения в основном писались на Java, было логично таргетить JVM. Время меняется, сейчас многие приложения пишутся далеко не на JVM, поэтому Kotlin надо туда.
— Джависты привыкли, что программы, написанные на Java 2, должны работать на Java 15. А в JS-мире готовы переписывать, там меньше приверженцев жёсткой обратной совместимости. Какая философия у Kotlin?
— Философия Kotlin прагматичная. Понятно, что «более стабилен — менее стабилен» — это такой континуум, и разные языки по-разному себя ведут. Даже Java, которую часто приводят как образец стабильности, конечно, совсем не образец и не стесняется в каком-нибудь релизе завести ключевое слово и поломать кучу старого кода или завести новые фичи JVM.
До сих пор меньшая часть enterprise-проектов перелетела через Java 9. Многие застыли на Java 8 — не от хорошей жизни, а потому что уже столько всего поломали, что хрен ты перепрыгнешь через девятку. В этом смысле там много примеров, как не надо делать.
Тема «жизни после Java 8» такая болезненная, что на JPoint об этом был отдельный доклад Триши Джи.
Но во многих смыслах Java — образец. Мы же учимся не только на своих, но и на чужих ошибках. Если видим, что Java что-то делает хорошо, гарантирует обратную бинарную совместимость, то мы это перенимаем. Если видим, что Java что-то делает плохо, поломала людям кучу кода, стараемся так не делать.
Видим, что Python всё поломал и до сих пор есть разрыв между комьюнити Python 2 и Python 3. И это страшная страшилка — так делать не надо, это плохо кончится фрагментацией сообщества.
Мы прагматично смотрим, что наша цель — чтобы Kotlin использовался и доставлял удовольствие большому числу программистов, поэтому стараемся понять, куда стоит идти, а куда нет.
— Любой слом кода — это недовольные люди, да?
— Да, но единственный способ ничего не ломать — это ничего не менять. Так не бывает. Вот это семантическое версионирование — полный миф. Любой, даже самый минорный багфикс кому-то что-то сломал бы, потому что кто-то заточился под конкретное поведение. Мы прекрасно это понимаем.
Релевантный выпуск xkcd
И то, что мы поделим фиксы на мажорные и минорные, во многом лицемерие. Нет такого. Есть «мы немножко поломали», «побольше поломали» и «сильно поломали». Вот чем отличаются релизы, в мажорном можно сломать побольше. А дальше у всех своё внутреннее ощущение, что значит «немножко» и «побольше».
В Kotlin есть хороший защитный механизм: благодаря Google есть структура Kotlin Foundation. Она во многом выполняет формальную роль, защищает торговую марку Kotlin от притязаний, но ещё выполняет важную вещь — в рамках неё работает language committee.
И в юрисдикции этого комитета находятся любые обратно несовместимые изменения в языке. Мы не имеем права сами решить что-то сломать, решение обязательно должен одобрить комитет.
Это хорошо, потому что заставляет 10 раз подумать, действительно ли тебе нужно это ломать, насколько это обоснованно, сформулировать, зачем я это ломаю, кому это принесет пользу, а может, надо не ломать, а оставить как есть. И получив у широкого комитета одобрение, только потом можешь ломать.
— Есть поверье, что именно комитетное управление в своё время сильно затормозило развитие Java. Как ты на это смотришь?
— Одно дело — комитетное развитие. На него смотрю очень плохо, и хорошо, что в Kotlin этого нет. Экспериментальные фишки и вообще новые фичи мы можем релизить сами, ни с кем не посовещавшись. У нас нет комитетного развития, но без комитета у нас нет права что-то сломать. А починить можем.
— В Java и JVM появляется много новых фич, влияет ли это как-то на эволюцию Kotlin или его планы?
— Безусловно влияет. Я уже сказал, что для нас JVM — важная платформа, и Kotlin называется так, потому что «K» — следующая буква после «J». Он и задумывался как Java 2.0. И никуда не делась цель дать людям, пишущим на Java, возможность лёгкой миграции на более современный язык.
Что означает лёгкая миграция? Наши пользователи — те, у кого миллионы строчек кода. Они не могут переписать свой код на Kotlin за один день. Поэтому есть явная цель: чтобы можно было, взяв проект на Java, адаптировать код постепенно. В большом проекте по-другому не получится.
А в проекте, например, уже новая Java. Нужна возможность использовать все новые фишки из Java в Kotlin. И более того, нужна возможность конвертировать свой Java-код в Kotlin, не теряя функциональность. Поэтому приходится следить за эволюцией в Java.
— В Kotlin уделяют много внимания продуктивности разработчика (чтобы он писал код быстрее), а насколько высок приоритет производительности (чтобы этот код исполнялся быстрее)?
— Конечно, мы не C++, у нас нет цели «zero cost abstractions». И нет цели делать системный язык для программирования низкого уровня, когда ты контролируешь каждый бит. Но перформанс нам не чужд. В любой современной разработке приложения тебе приходится работать с большими данными, списками. И важен, скорее, асимптотический перформанс.
Естественно, рассматривая фичу, мы оцениваем, сколько она стоит. И если она приносит нелинейную сложность, куда-то зарыта большая цена, так делать не будем. Но производительность фич никогда не является самоцелью.
Первоочередная цель — продуктивность программиста. Чтобы когда пришли с бизнес-задачей и сказали «надо что-то сделать», ты мог эту мысль донести прямо до кода.
Но в Kotlin есть задача, чтобы если потом в результате профилирования выяснилось, что в этом месте что-то подтормаживает, то были инструменты это исправить. А не как в Python, где если ты уткнулся в перформанс, то идёшь переписывать на C, другого варианта нет.
— В Java давно обещают завезти Project Loom — насколько это событие для Kotlin, где есть корутины? Возможно ли реализовать корутины на Loom?
— Во-первых, надо понимать, что Project Loom — это библиотечная фича JVM-платформы, а не языковая. Поэтому с ней интеграция намного проще, не нужно вносить изменения в язык.
И второе, что надо понимать: хотя кажется, что Project Loom и корутины для одной бизнес-области, и оба про какое-то асинхронное программирование, цели этих проектов очень разные. И трейд-оффы в отношении производительности тоже в итоге разные.
Project Loom в первую очередь нацелен на легаси Java-код. В Java-экосистеме огромное количество кода написано блокирующим образом, и это надо как-то масштабировать. А делать это тяжело, потому что каждый поток — это много ресурсов. И задача Project Loom — взять старый Java-код и легко заскейлить благодаря JVM-фиче виртуальных легковесных потоков.
Естественно, в основном это код, который делает I/O, поэтому он редко засыпает. Чаще всего работает без остановки, выполняет какую-то логику и иногда натыкается на то, что надо подождать I/O.
У Kotlin стояла другая задача. Мы видим, что в экосистеме Java растёт огромное число асинхронного программирования. В это вкладываются Spring, Vert.x, куча людей. Это код, который постоянно засыпает, просыпается, делает микрозадачи. Он выполняет много маленьких кусочков задачи, переключаясь между исполнением разных вещей.
И всё хорошо, но кода получается многовато, он неудобный, простому программисту сложно разобраться. Все говорят «Мне ваш Rx или ещё что-то нравятся, но в этом очень сложно разобраться, слишком много всего». И понятно, почему — языковой поддержки нет, получается такая callback-лапша, даже если колбэки запрятаны.
Задача корутин была решить эту проблему на языковом уровне. Дать механизм, позволяющий писать этот код более понятным для человека образом, не теряя всех преимуществ и наработок уже существующей активной экосистемы. Дизайн Kotlin в первую очередь — удобная интеграция с этой существующей экосистемой, чтобы взять эти уже написанные библиотеки и более удобно их использовать из Kotlin.
То есть другая цель. И другое применение. Когда начинаешь смотреть в UI, что там делает активный и асинхронный код? Он асинхронный не потому, что постоянно делает I/O, а потому что летит куча ивентов, и куча маленьких кусков работы между переключениями. Чуть-чуть поработали и уснули, чаще всего мало работы и много переключений.
Поэтому трейдоффы в производительности другие. Корутины Kotlin не очень работают с кодом, который редко спит, а Project Loom наоборот. Мы проводили эксперименты. Loom пока недоступен даже в виде экспериментальной фичи, но можно выкачать самому, собрать билд и поиграться. Мы поигрались и подтвердили свою интуицию. Если начать делать реактивные стримы через Loom и постоянно их будить, то продолбаешь столько перформанса, что зачем тебе это? А если делать в Project Loom то, для чего он заточен, код, который будет иногда засыпать, то он хорошо себя ведёт.
Project Loom особо не нужен новый синтаксис, это библиотечная штука. Ты стартуешь виртуальный поток, I/O-операция в нём стоит тебе дёшево, потому что она заблокирует виртуальный поток. Мы сделаем библиотечную поддержку этого.
Сейчас в корутинах есть такое неудобство, что если пишешь асинхронный код с корутиной и хочешь вызвать блокирующую функцию (какой-нибудь парсер, который будет что-нибудь подсчитывать с сокета), — неудобно, что ты должен это написать withContext(Dispatchers.IO). Чтобы выделить для этого специальный пул, который не страшно заблокировать.
То есть при работе с корутинами, если операция JVM-блокирующая (какая-нибудь сокетная), ты либо вынужден искать асинхронную альтернативу, взять Java-библиотеку, либо завернуть блокирующий вызов в специальный wrapper, обозначить, что здесь будешь блокироваться.
И Project Loom позволит не требовать такой заворот, мы сможем запустить корутину в виртуальных средах, и так как они виртуальные, их можно будет блокировать безопасно. Потому что ты блокируешь виртуальный поток.
Это позволит писать такой код элегантнее, не размечая блокирующих. Это круто, мы с нетерпением ждем, когда JVM зарелизит Project Loom, но когда это случится, никто ответить, к сожалению, не может.
На Joker 2020 о Project Loom подробно рассказал Алан Бейтман (Oracle), для Хабра был сделан текстовый перевод. И в апреле на JPoint снова будет спикер из Loom: о потоках поговорит technical lead этого проекта Рон Пресслер.
А о корутинах ещё в 2018-м у нас рассказывал сам Роман, доступна видеозапись.
— Тренды последних лет — это функциональщина, реактивщина. Что будет следующим трендом? Что нужно изучить, чтобы через год стать пионером в самом зарплатном сегменте? С тебя предсказания.
— Понятия не имею, я же не футурист, и хрустального шара у меня нет. Давайте посмотрим, что происходит сейчас. Тут надо понимать, что тренд реактивщины и функциональщины пока только в начале. Например, только недавно стали популярны реактивные UI-фреймворки. React, SwiftUI, Jetpack Compose и так далее.
Они стали популярны, но некоторые ещё только в разработке — Jetpack Compose ещё не дошел до релиза. Мы видим, что реактивные UI-фреймворки предъявляют к языкам новые требования, которых раньше не было.
И, например, специально для поддержки React в JavaScript чуваки в Facebook совершенно сбоку запилили JSX — целое расширение языка. В SwiftUI зарелизили жирный апдейт, они затащили в язык целый паровоз фич только для того, чтобы язык смог поддерживать этот реактивный UI.
Мы в Kotlin не так делаем, но ребята из Google, которые работают над Jetpack Compose, написали плагин неслабого масштаба к компилятору. То есть фактически тоже фича в языке, но просто она прилетает плагином. Это уже не просто библиотечная функция, это целый плагин к компилятору, который всё это умеет делать. Мы видим, что это новый челлендж для языков. Не существовало языков, которые это нативно поддерживают.
Комьюнити языковых дизайнеров осознаёт: «Чёрт побери, у нас есть реактивные фреймворки, и мы должны в дизайне языков как-то учитывать их требования!». Никогда такого не было, языки ещё не готовы к этому, они только учатся. Что надо сделать такого в языке, чтобы это всё заходило?
— Тем не менее, это реальность, как подростковый секс: все про него говорят, но никто не пишет.
— Мы с этой реальностью в самом начале пути, со всеми этими проблемами ещё даже не стали думать: этот реактивный UI хорошо, а как нам сделать теперь реактивный бэкенд? Как это должно выглядеть?
— А надо ли это? Идти же надо от запроса.
— Конечно, надо. Мне очень нравится цитата Форда: «Если бы я, когда делал свою Ford Model T, слушал бы людей и спросил бы у них, что вы хотите, они бы сказали “мы хотим более быстрых лошадей”».
Здесь такая же проблема. Ты говоришь «запрос», здесь нет никакого запроса. Люди писали говнокод, 10 страниц кода UI, и ни у кого не возникало мысли, что можно написать 10 строчек, а всё остальное за тебя сделает реактивный фреймворк.
— Кто-то по этому скучает.
— Это пожалуйста, это далёкие от меня проблемы, мы про продуктивность. Если можно вместо 10 страниц кода написать 10 строчек — мы за это. То же самое с асинхронщиной. Знаешь, какой для нас был прорыв в асинхронщине, когда мы поняли, что правильно идём с корутинами? Когда у нас был перед глазами проект Netty, где парсер HTTP-запроса — это, не шучу, страниц 20 текста. Просто машина, которая парсит HTTP-запрос — 20 страниц, в которых, кроме суперумного Нормана, вообще никто не разберётся. Надо быть эпическим гением вроде Нормана, чтобы написать и отладить этот код.
А у нас в Ktor страничка кода то же самое делает. Всего одна! Её может написать любой среднестатистический программист, который прочитал спеку, что такое HTTP-заголовок. Вот куда мы стремимся.
Как сказал Роман, сейчас JVM — основной рынок для Kotlin. Значит, если вы прочитали этот текст, то с высокой вероятностью связаны с JVM-миром. Тогда вам будет интересно на нашей Java-конференции JPoint (13-17 апреля, онлайн).
А если вы Android-разработчик, то вам подходит конференция о мобильной разработке Mobius (13-16 апреля).
И даже если вы дотнетчик, JS-разработчик или тестировщик, для вас в апреле мы тоже проведём конференции.
nehaev
100 человек — это все только сотрудники JB или независимые контрибьюторы тоже?
antonarhipov
сотрудники JB