class: center middle kotlin-bg # .title[Kotlin] ??? Здраствуйте! Меня зовут Залим! Я сегодня расскажу вам про Котлин. --- class: kotlin-is ### Kotlin это ??? Что же такое котлин? -- Остров в Финском заливе:  ??? Если верить википедии есть такой остаров в финском заливе. И на нем распологается город Кронштадт --- class: kotlin-is ### Kotlin это ??? Но! это не все! Еще википедия говорит что есть такая деревня в Польше -- Деревня в Польше:  ??? --- class: kotlin-is ### Kotlin это Кетчуп:  ??? И в той же Польше есть кетчип Kotlin --- class: kotlin-is ### Kotlin это Класс эскадренных миноносцев:  ??? Еще это название класса эскадренных миноносцев времен холоднй войны --- class: kotlin-is-lang ### Kotlin это Язык программирования  ### [Kotlinlang.org](https://kotlinlang.org) ??? А еще! Это язык программирования! Про него мы сегодня и поговорим! Кто слышал про Котлин? А кто программировал на нем? Кто знает что код на котлине можно компилировать в JavaScript? --- class: center, middle ratio: 16:9 # Kotlin как язык для разработки фронтенда ### Залим Башоров .twi[[@bashorov](https://twitter.com/bashorov)]
.link-to-slides[ Временная ссылка на слайды: [zal.im/s](http://zal.im/slides/holyjs17) ] ??? Я вам расскажу про Котлин и о том как использовать его для разработки фронтенда *** кто слышал про котлин? кто программировал? кто слышал Kotlin JS кто использовал Kotlin JS? кто знаком с ES 2015? кто знаком с TS? --- class: center middle .my-avatar[] .about-me-kotlin[] .about-me-jetbrains-min[] ### Залим Башоров zalim.bashorov@jetbrains.com .twi[[@bashorov](https://twitter.com/bashorov)] Руковожу направлением Kotlin JS ??? Я работаю в команде Kotlin в компании JetBrains. Разработываю Котлин на Котлине уже почти 5 лет. Руковожу направлением Kotlin JS. --- # Kotlin -- - Статически типизированный -- - Современный -- - Прагматичный ??? 0 *** Выразительный Безопасный Гибкий - modern -- concise -- safe --- billion dollar mistake ---- compile time error instead of runtime -- expressive - progmatic -- tooling --- IDE (completion, navigation, refactorings, inspections) -- interop TIOBE? --- # Kotlin targets -- - Java byte code ??? Котлин может компилироваться в Java byte code и запускаться на JVM, будь это сервер, или андроид устройство -- - Официальный язык разработки для Android -- - JavaScript ??? может компилироваться в JavaScript и соответственно запускаться везде где есть JavaScript -- - Native ??? также, с недавних пор, котлин компилируется в нативный код и программа написанная на котлине может быть запущена независимо от наличия виртуальной машины *** raspberry pi, ios? --- # Kotlin JS -- Генерируемый код * совместим с ECMAScript 5.1 ??? Сейчас компилятор Kotlinа умеет генерировать код только для ECMAScript 5.1, Это позволяет запускать скомпилированный код на большом спектре браузеров / устройств -- * поддержка более поздних версий ECMAScript запланирована ??? Мы плнируем в будущем поддержат более поздние версии ECMAScript -- * всегда содержит "use strict" ??? Генерируемый код совместим со "strict mode" И всегда содежит "use strict" Данная директива включает дполонительные ограничения на код который выполняется в JS VM. Что во-первых делает код безопаснее, а во-вторых может помочь VM лучше оптимизировать код. -- * быстрый ??? Мы очень стараемся пораждать код который хорошо оптимизируетсяся современными JS VM -- * читаемый (на сколько это возможно) ??? Так же, мы стараемся не обделять вниманием и читаемость получаемого кода, но к сожалению, этот пункт страдает чаще остальных. В основном это связано с различием в семантике языков. Например, в котлине можно перегружать функции, т.е. иметь несколько функций с одним и тем же названием, а в JS такого нет. Эту проблему можно решить по разному. Например, можно эмулировать это поведение проверяя тип аргументов на рантайме, как часто делают в JS. А можно генерировать индивидуальное имя для каждой функции. У каждого метода есть свои плюсы и минусы. Мы выбрали второй потому что он быстрее и надежнее. -- * есть поддержка SourceMaps ??? Есть поддержка SourceMaps. Это означает что можно взять любой JavaScript дебагер, поддерживающий sourcemaps, и дебажить вашу программу прямо по котлиновским сорцам и не важно что там на самом деле сгенерилось. --- class: center middle # Введение в Kotlin на примерах ??? Давайте, для начала, немного изучим Котлин. И будем мы это делать на примерх. --- layout: true class: compare-kotlin-to-js-h --- # Формат введения ```kotlin // код на Kotlin ``` ```javascript // код на JavaScript ``` ??? ??? Формат будет такой: Будет фрагмент кода на Котлине и рядом фрагмент с таким же смыслом но на JavaScript. -- .ES2015[ ```javascript // код c использованием фич ECMAScript 2015 ``` ] ??? иногда будет код на ES2015 -- ```ts // код на TypeScript ``` ??? еще реже на TypeScript Чтобы вам было проще ориентироваться в правом верхем углу каждого блок есть значок используемого языка --- # Замечания ??? Но прежде чем мы начнем хочется отметить что -- * Это не батл между языками ??? Это не батл между языками и нет цели показать или доказать что какой-то язык лучше или хуже -- * Цель - объяснить семантику ??? цель - объяснить семантику кода на Котлине в теримнах другого языка в плане поведения на рантайме, какие могут быть ожидания, например по безопасности. -- * Результат трансляции может отличаться ??? Результат трансляции фрагмената на Котлине может отличаться от того кода который приведен как пример на JavaScript. -- * Это не весь Kotlin ??? Ввиду ограниченности времени я не могу рассказать все про Котлин, я расскажу только про маленькую часть языка. Так же, некоторые примеры, осознано упрощены, или буду умалчивать какие-то детали. Но вы не пугайтесь, язык простой! --- # Переменная ??? Давайте начнем с объявления переменных --- # Переменная ```kotlin var ``` ??? для объявления переменной нужно написать ключевое слово var --- # Переменная ```kotlin var `foo` ``` ??? далее желаемое имя --- # Переменная ```kotlin var foo`: Int` ``` ??? тип это переменной, используя двоеточие и название типа --- # Переменная ```kotlin var foo: Int `= 1` ``` ??? и значение для инициализации Можно не инициализировать переменную сразу, а сделать это позже. Компилятор проконтролирует что переменная инициализированна до первого использования. --- # Переменная ```kotlin var foo: Int = 1 ``` ```javascript var foo = 1; ``` ??? Аналог на JS На самом деле часто тип можно не писать и компиялтор сам выведет тип И тогда наш код упростится до такого --- # Переменная без явного указания типа ```kotlin var foo = 1 ``` ```javascript var foo = 1; ``` ??? Кто найдет 2 отличая? А одно? *** TODO: accessors --- # Переменная без явного указания типа ```kotlin var foo = 1 ``` ```javascript var foo = 1`;` ``` ??? Да, первое отличие в том что в примере на котлин отсутсвует ";" и это не ошибка В Котлине тоже ";" опциональна, но в отличае от JS никто не будет советовать его использовать всегда Так, на всякий случай, как бы чего не вышло. Более того IDE будет форсит убирание лишних ";" Попишешь так денек на котлине потом начинаешь постоянно забывать ";" в коде на других языках Я серьезно! Будьте осторожны! --- # Переменная (ES 2015) ```kotlin var foo = 1 ``` ```javascript var foo = 1; ``` .ES2015[ ```javascript `let` foo = 1; ``` ] ??? Второе отличие, на самом деле, в коде не видно. Оно заключается в том что семантика var в Котлине и в JS отличаются. А в частности область их видимости, в JS это объемлющая функция, а в Котлине это объемлющий блок. Более близкий аналог это let добавленный в ES2015. Так же, стоит отметить что в JS инициализатор для переменных не объязателен и тогда значением переменной будет undefined Хотя и считается плохим тоном(???). проверятеся линтерами ??? В Котлине переменную можно не инициализировать переменную сразу, достаточно это сделать до первого использования. --- # Неизменяемая переменная ```kotlin `val` foo = 1 ``` .ES2015[ ```javascript const foo = 1; ``` ] ??? Для того что бы сделать нашу переменную неизменяемой нужно поменять ключевое слово var на val Близкий по смыслу это const введенный в ES2015, но const всегда должен быть с инициализатором, а val в Котлине можно инициализировать позже. Главное сделать это перед первым использованием. --- # Nullable/not-null types ```kotlin val a: String = "ok?" // OK val b: String = null // Ошибка компиляции ``` -- ```kotlin val c: String`?` = "ok?" // OK val d: String`?` = null // OK ``` --- # Удобная работа с nullable значениями ### Safe call ```kotlin var foo: String? = ... val a = foo`?.`length val b = foo`?.`substring(1) ``` ```js var foo = ... var a; if (foo != null) a = foo.length else a = null var b; if (foo != null) b = foo.substring(1) else b = null ``` --- # Удобная работа с nullable значениями ### Elvis operator ```kotlin var foo: String? = ... val a = foo `?:` "foo is null" val b = foo?.substring(1) `?:` "Kool" ``` ```js var foo = ... var a; if (foo != null) a = foo else a = "foo is null" var b; if (foo != null) b = foo.substring(1) else b = "Kool" ``` --- # Функция ??? теперь перейдем к функциям. --- # Функция ```kotlin `fun` ``` ??? для объявления функций используется ключевое слова fun --- # Функция ```kotlin fun `sum` ``` ??? Далее имя функции --- # Функция ```kotlin fun sum`(a: Int, b: Int)` ``` ??? Аргументы разделенные запятыми с указанием имени и типа для них --- # Функция ```kotlin fun sum(a: Int, b: Int)`: Int` ``` ??? И в конце указывается тип возвращаемого значения Если функция ничего не возвращает, можно ничего не писать --- # Функция ```kotlin fun sum(a: Int, b: Int): Int `{` return a + b `}` ``` ??? и в фигурных скобках пишем код, как и в многих других языках -- ```javascript function sum(a, b) { return a + b } ``` ??? Аналогичный код на JS будет выглядить так --- # Функция в одну строчку ```kotlin fun sum(a: Int, b: Int): Int = a + b // ``` ```javascript function sum(a, b) { return a + b } ``` ??? Если нужен только вернуть значение какого-то выражения можно записать функцию короче --- # Функция в одну строчку ```kotlin fun sum(a: Int, b: Int)`: Int` = a + b // ``` ```javascript function sum(a, b) { return a + b } ``` ??? Тип возвращаемого значения тоже можно опустить в данном случае, компилятор выведет сам. --- # Функция в одну строчку ```kotlin fun sum(a: Int, b: Int) = a + b // ``` ```javascript function sum(a, b) { return a + b } ``` ??? Пример на JS тоже можно конечно переписать, но семнтика немного поменяется. А именно С помощью такой стрелочной функции невозможно написать мембер класса который работал бы с this. А для статических функций разница не значительная -- в функция не будет доступен до инициализации, но это больше про семантику const. -- .ES2015[ ```js const sum = (a, b) => a + b ``` ] --- # Функция как значение ??? Во многих современных языках можно использовать функцию как значение. И Котлин не исключение. В разных языках такие функции называются по разному где-то function expression, где-то closure, где-то еще как-то, я буду называть из lambda --- # Функция как значение ```kotlin val lambda = `{` i: Int, s: String -> doSomething() `}` ``` ```javascript const lambda = `(`i, s) => { doSomething() `}`; ``` ??? лямбда объявлется с помощъю фигурнах скобок --- # Функция как значение ```kotlin val lambda = { i: Int, s: String `->` doSomething() } ``` ```javascript const lambda = (i, s) `=>` { doSomething() }; ``` ??? список параметров и тело лямбды разделены стрелочкой --- # Функция как значение ```kotlin val lambda = { i: Int`,` s: String -> doSomething() } ``` ```javascript const lambda = (i`,` s) => { doSomething() }; ``` ??? Параметры, между собой, разделяются запятой --- # Функция как значение ```kotlin val lambda = { i: Int, s: String -> doSomething() } ``` ```javascript const lambda = (i, s) => { doSomething() }; ``` ??? Лямбда сама по себе не интересна, нужно ее как то уметь вызвать. Вызов лямбды выглядит так же как вызов обычной функции. -- ### Вызов: ```kotlin lambda(1, "string") ``` --- # Функция как параметр другой функции (1) ??? другой интресеный вариант использования ламбд это их передача в другие функции -- ```kotlin fun foo(f: `(s: String)` `->` `Unit` ) {...} ``` ??? объявим функцию foo которая принимает лямбду f которая, в свою очередь, принимает строку и ничего не возвращает --- # Функция как параметр другой функции (1) ```kotlin fun foo(f: (s: String) -> Unit) {...} fun usage() { `foo({ s -> doSomething(s) })` } ``` ```javascript function foo(f) {...} function usage() { `foo((s) -> doSomething(s))` } ``` ??? теперь вызовем эту функцию. Часто вызвов можно записать короче. Давайте попробуем --- # Функция как параметр другой функции (1) ```kotlin fun foo(f: (s: String) -> Unit) {...} fun usage() { foo`(`{ s -> doSomething(s) }`)` } ``` ```javascript function foo(f) {...} function usage() { foo((s) -> doSomething(s)) } ``` ??? В котлине при вызове функции последний параметр которого это лямбда, можно тело лямбды написать снаружи скобочек --- # Функция как параметр другой функции (1) ```kotlin fun foo(f: (s: String) -> Unit) {...} fun usage() { foo`()` { s -> doSomething(s) } } ``` ```javascript function foo(f) {...} function usage() { foo((s) -> doSomething(s)) } ``` ??? А сами скобки можно убрать если они пустые. --- # Функция как параметр другой функции (2) ```kotlin fun foo(f: (s: String) -> Unit) {...} fun usage() { `foo { s -> doSomething(s) }` } ``` ```javascript function foo(f) {...} function usage() { foo((s) -> doSomething(s)) } ``` ??? И палучим такое. Такая запись очень похожа на языковую конструкцию и часто используется для написания красивого API, DSLей --- # Type-Safe Html ```kotlin html { head { title {+"HTML encoding with Kotlin"} } body { h1 {+"HTML encoding with Kotlin"} p {+"this format can be used as an alternative markup to HTML"} // an element with attributes and text content a(href = "http://kotlinlang.org") {+"Kotlin"} // content generated by p { for (arg in args) +arg } } } ``` --- # Функция как параметр другой функции (2) ```kotlin fun foo(f: (s: String) -> Unit) {...} fun usage() { foo { `s` -> doSomething(`s`) } } ``` ```javascript function foo(f) {...} function usage() { foo((s) -> doSomething(s)) } ``` ??? Продолжем соращать наш код. В случае если параметр один его можно не выписывать, а вместо него использовать идентификатор it --- # Функция как параметр другой функции (3) ```kotlin fun foo(f: (s: String) -> Unit) {...} fun usage() { foo { doSomething(`it`) } } ``` ```javascript function foo(f) {...} function usage() { foo((s) -> doSomething(s)) } ``` --- # Функция как параметр другой функции (4) ```kotlin fun foo(f: (s: String) -> Unit) {...} fun usage() { foo(`::doSomething`) } ``` ```javascript function foo(f) {...} function usage() { foo(`doSomething`) } ``` ??? А еще вместо того чтобы писать лямбду в данном примере можно просто взять и передать ссылку на функцию doSomething --- # Условные операторы ??? Линейный код писать не интересно, иногда хочется принимать решение. --- # Условные операторы: if ```kotlin if (condition) { ... } else { ... } ``` ```js if (condition) { ... } else { ... } ``` ??? Как и во многих языках есть условный оператор if, синтаксис такой же как и в JS. --- # Условные операторы: if ```kotlin if (condition) ... else ... ``` ```js if (condition) ... else ... ``` ??? Как и во многих языках фигурный скобки опциональны --- class: compare-kotlin-to-js-v # Условные операторы: when ```kotlin when { condtion1 -> doSomething1() condtion2 -> { doSomething2() } else -> doSomethingElse() } // ``` ??? Еще один способ записывать условия это опратор when -- ```js if (condtion1) doSomething1() else if (condtion2) { doSomething2() } else doSomethingElse() } ``` ??? С аналогом на JS сложно, в общем случае это лесенка if'ов Иногда можно заменить на switch. *** TODO: other when variations? Кстати про switch --- # Условные операторы как выражения: if ```kotlin val message = if (timeOfDay == "morning") "Hi!" else "Bye!" ``` ```js const message = timeOfDay === "morning"? "Hi!" : "Bye!" ``` ??? В котлине нет тернартного оператора, но условные операторы можно использовать в выражениях --- # Условные операторы как выражения: if (2) ```kotlin val message = if (timeOfDay == "morning") { doSomething() "Hi!" } else { doSomethingElse() "Bye!" } ``` ??? Можно писать даже так. Как думаете почему может быть полезным писать так? Например чтобы создать неизменямую переменную с относительно сложной логикой инициализации --- # Условные операторы как выражения: if (3) ```kotlin val message: String if (timeOfDay == "morning") { doSomething() message = "Hi!" } else { doSomethingElse() message = "Bye!" } ``` ??? Можно переписать пример так воспользовашись тем что котлин позволяет объявить переменную, а инициализировать позже. Ну а с этим примерм то что не так? Зачем вставлять if в выражение? --- # Условные операторы как выражения: if (3) ```kotlin val message`: String` if (timeOfDay == "morning") { doSomething() `message =` "Hi!" } else { doSomethingElse() `message =` "Bye!" } ``` ??? Лично мне, такой вариант кажется многословным. Приходится явно указывать тип, в каждой ветке писать инициализацию. --- # Условные операторы как выражения: when ```kotlin val message = when (timeOfDay) { "morning" -> "Hi!" "evening" -> "Bye!" else -> null } ``` ```js /* ¯\_(ツ)_/¯ */ ``` ??? when тоже можно использовать в выражениях И сново есть сложности с аналогом на JavsScript --- # Условные операторы как выражения: when ```kotlin val message = when (timeOfDay) { "morning" -> "Hi!" "evening" -> "Bye!" else -> null } ``` .ES2015[ ```js let message; switch(timeOfDay) { case "morning": message = "Hi!"; break; case "evening": message = "Bye!"; break; default: message = null; } ``` ] ??? В данном слуае можно воспользоваться switch но не всегда это возможно из-за семантики сравнения в JS switch Так же, в примере на JS приходится использовать let вместо const, т.е. переменная будет, по факту, изменяемой. --- # Еще выражениям являются: * `try`-`catch`-`finally` * `throw` * `return` ??? --- # Циклы: while ```kotlin while (condition) { // do something } ``` ```js while (condition) { // do something } ``` --- # Циклы: do while ```kotlin do { // do something } while (condition) ``` ```js do { // do something } while (condition) ``` --- # Циклы: do while (2) ```kotlin do { `val foo = ...` // do something } while (`foo == 0`) ``` ```js { `let foo = ...` do { // do something } while (`foo === 0`) } ``` --- # Циклы: for (1) ```kotlin for (`i in 0..10`) { // do something } ``` ```js for (var i = 0; i <= 10; i++) { // do something } ``` --- # Циклы: for (2) ```kotlin for (`element in collection`) { // do something } ``` .ES2015[ ```js for (element of collection) { // do something } ``` ] --- # Строки ```kotlin "Hello, HolyJS!" ``` ```js "Hello, HolyJS!" ``` --- # Многострочные строки ```kotlin """Hello, HolyJS!""" ``` .ES2015[ ```js `Hello, HolyJS!` ``` ] --- # Строковые шаблоны ###(template literals, string interpolation) ```kotlin val name = ... "Hello, $name" "Hello, ${name.firstChar()}" ``` .ES2015[ ```js const name = ... "Hello, ${name}" "Hello, ${name.firstChar()}" ``` ] --- exclude: true Операторы +, -, *, /, +=... ++,.. , ==, ===, >, <, >= <= in !in is !is as as? --- # Класс ??? Перейдем к классам --- # Класс ```kotlin class ``` ??? для объявления классов используется ключевое слово class --- # Класс ```kotlin class `MyClass` ``` ??? имя класса --- # Класс ```kotlin class MyClass `{` `}` ``` ??? И тело класса в фигурных скобках --- # Класс ```kotlin class MyClass { `val foo = "text"` `fun bar(a: String) = a + foo` } ``` ```javascript class MyClass { constructor() { `this.foo = "text";` } `bar(a) {` ` return a + this.foo;` `}` } ``` ??? класс может содерржать уже привычные нам проперти и функции --- # Класс: конструктор ```kotlin class MyClass { val foo: String `constructor(foo: String) {` ` this.foo = foo` ` doSomething()` `}` } ``` ```javascript class MyClass { `constructor(foo) {` ` this.foo = foo;` ` doSomething()` `}` } ``` ??? можно еще написать конструктор --- # Класс: много конструкторов ```kotlin class MyClass { val foo: String constructor(foo: String) { ... } `constructor(foo: Boolean) {` this.foo = "Boolean: " + foo doSomethingForBool() } `constructor(foo: Int) : this(foo.toString()) {` doSomethingForInt() } } ``` ??? много конструкторов --- # Класс: primary конструктор ```kotlin class MyClass`(foo: String)` { } ``` ```javascript class MyClass { constructor(foo) { } } ``` ??? В языке есть понятние primary constructor это конструктор объявленный сразу после имени класса Зачем это надо? во первых это "красиво", на самом деле дело не в красоте а удобстве --- # Класс: primary конструктор ```kotlin class MyClass(foo: String) { `val bar = foo` } ``` ```javascript class MyClass { constructor(p) { `this.bar = p;` } } ``` ??? на проперти из primary констуктора можно ссылаться из инициализаторов переменных --- # Класс: primary конструктор ```kotlin class MyClass(foo: String) { val bar = foo val foo: String init { `this.foo = foo` } } ``` ```javascript class MyClass { constructor(p) { this.bar = p; `this.foo = p;` } } ``` ??? и инит блока --- # Класс: свойство в primary конструкторе ```kotlin class MyClass(`val foo: String`) ``` ```javascript class MyClass { constructor(foo) { this.foo = foo; } } ``` ??? но это не все, можно объявлять свойство прямо в primary конструкторе и это ОЧЕНЬ удобно -- ```ts class MyClass { constructor(`public foo: string`) { } } ``` ??? В TypeScript есть похожая фича --- class: compare-kotlin-to-js-v # Интерфейс ```kotlin `interface` Foo { val bar: Int fun baz(i: Int): String } ``` ??? Еще есть интерфейсы, для их объявления используеся ключевое слово interface Попроперти теперь пишем без инициализатора и функции без тела. В JS нет синтаксического аналога. Все работает по договоренности. Договорились что функция ожидает объекты у которых есть свойство bar и функция baz Возможно даже договоренность будет где-то зафиксирована ввиде документации или JsDoc -- ```typescript interface Foo { bar: number baz(i: number): string } ``` ??? Интерфейсы есть в TypeScript --- class: compare-kotlin-to-js-v # Интерфейс ```kotlin interface Foo { val bar: Int fun baz(i: Int): String } //... `myvar is Foo` // OK ``` ```typescript interface Foo { bar: number baz(i: number): string } //... `myvar instanceof Foo` // Error ``` ??? Но сущусивуют они только на этапе компиляции, а Котлиновские интерфейсы можно использовать для проверки типа на рантайме И накладные расходы от этого минимальны :) Другое отличие в том что в TypeScript структурная типизация. Т.е. там где ожидается Foo можно использовать любой объект с похожей структурой -- с полем bar и функцией baz А в Котлине немного строже, этот объект долден быть инстансом класса который реализует нужный интерфейс, а не похожий интерфейс! --- # Интерфейс с кодом * Может содержать реализацию * Но не может иметь состояния ```kotlin interface Foo { val bar `get() = 42` fun baz(i: Int) `= i.toString()` val boo `= 51 // Ошибка компиляции` } ``` ??? Еще интерфейс может содержать реализацию для акксессоров проперти и для функций Но не может иметь состояние --- class: compare-kotlin-to-js-v # Наследование ??? Давайте теперь научимся комбинировать классы и интерфесы -- * Классы и их члены `final` по умолчанию ```kotlin interface A { fun foo(): String } `open` class B : A { override fun foo() = "B.str" `open` val bar = 1 } ``` ??? Классы и их мемберы `final` по умолчанию и для того чтобы от них можно было наследоваться и переопределять необходимо их пометить как open --- class: compare-kotlin-to-js-v # Наследование ```kotlin interface A { fun foo(): String } open class B : A { `override` fun foo() = "B.str" open val bar = 1 } ``` ??? Для того чтобы переопредлить что-то у предка нужно воспользоваться ключевым словом override --- class: compare-kotlin-to-js-v # Наследование ```kotlin interface A { fun foo(): String } open class B : A { override fun foo() = "B.str" open val bar = 1 } interface C { ... } `class D : B(), C` { override fun foo() = "D.str" override val bar = 2 } ``` ??? Предки отделяются от названия класса с помощью ":" Если их нескольколько то они перечеляются через запятую --- class: compare-kotlin-to-js-v # Наследование ```kotlin interface A { fun foo(): String } open class B : A { override fun foo() = "B.str" open val bar = 1 } interface C { ... } class D : B(), C { override fun foo() = "D.str" override val bar = 2 } ``` ```js class B { constructor() { this.bar = 1 } foo() { return "B.str" } } class D extends B { constructor() { this.bar = 2 } foo() { return "D.str" } } ``` ??? В JavaScript интерфейсов нет поэтому привожу пример только с классами --- class: compare-kotlin-to-js-v # Наследование на TypeScript ```ts interface C { bar: number } class D extends B implements C { constructor() { super() this.bar = 2 } foo() { return "D.str" } } ``` ```ts interface A { foo(): string } class B { bar: number constructor() { this.bar = 1 } foo() { return "B.str" } } ``` ??? И аналогичный пример на TypeScript --- # Взаимодействие со внешним миром ??? Мы уже умеем писать код на котлине, но для написания чего-то реального, полезного нам необходимо нучиться взаимодействовать с окружающим миром. Этот процесс можно условно разбить на две части -- * Вызов JavaScript кода из Kotlin -- * Вызов Kotlin кода из JavaScript --- # Вызов JavaScript кода из Kotlin ??? Есть несколько способов вызова JavaScript кода из Котлина -- * Функция `js` ??? Функция `js`, я еще ее называю "магической", скоро вы поймете почему -- * Тип `dynamic` ??? Тип `dynamic` -- * Типизированные декларации ??? И Типизированные декларации -- (Способы перечислены от наименее безопасного к наиболее) ??? Отличаются эти способы степеню (статической) безопасности и выразительности Здесь они перечислены от наименее безопасного к наиболее Понятно что горантии являются источником ограничений. Т.е. чем больше у нас горантий тем больше ограничений и тем меньше гибкости, выразительности. --- class: compare-kotlin-to-js-v # Функция `js` (1) ??? Остановимся на каждом из способов подробнее. Начнем с магической функции js -- * Функция принимает константную строку ??? Функция принимает только константную сроку. -- * Строка на этапе компиляции парсится и встраивается в АСТ ??? строка на этапе компиляции парсится и встраивается в АСТ из которого впоследствии получается код -- * Это не `eval` и не `prepack` ??? Это не `eval` в итоговом коде будет то что написано в строке, без каких оптимизаций и предвычислений -- ```kotlin js("console.log('hello')") ``` ??? Например, в результате компиляции кода на котлине слева получим такой код на JS -- ```js console.log('hello') ``` --- # Функция `js` (2) * Строка проверяется на синтаксическую корректность ??? Еще раз повторюсь строка не просто вставлется как есть а парсится и тем самым проверяется на синтаксическую корректность. Т.е. в эту функци можно передать только константную строку с синтаксически корректным JS кодом -- ```kotlin js("console log") ``` ??? Например для такого кода компилятор покажет ошибку -- Компилятор покажет ошибку: ```bash error: JavaScript: missing ; before statement js("console log") ^ ``` --- # Тип `dynamic` ??? для удобного взаимодействия с JavaScript'ом добавили специальный динамичиский тип -- * Доступен только в Kotlin JS ??? Данный тип доступен только при компиляции в JS --- # Тип `dynamic` * .gray[ Доступен только в Kotlin JS ] * Значение типа `dynamic` можно присваивать куда угодно ```kotlin var d: dynamic = ...; var i: Int = 1; var s: String = "" `i = d` // OK `s = d` // OK `i = s` // Error `s = i` // Error ``` ??? Значение типа `dynamic` можно присваивать куда угодно --- # Тип `dynamic` * .gray[ Доступен только в Kotlin JS ] * .gray[ Значение типа `dynamic` можно присваивать куда угодно ] * В переменную типа `dynamic` можно писать что угодно ```kotlin var d: dynamic = ...; var i: Int = 1; var s: String = "" `d = i` // OK `d = s` // OK `d = null` // OK ``` ??? В переменную типа `dynamic` можно писать что угодно --- # Тип `dynamic` * .gray[ Доступен только в Kotlin JS ] * .gray[ Значение типа `dynamic` можно присваивать куда угодно ] * .gray[ В переменную типа `dynamic` можно писать что угодно ] * Можно вызывать любую функцию и обращаться к любому полю ```kotlin var d: dynamic = ...; `d.foo()` `d.bar(1)` `d.baz = "z"` ``` ??? Можно вызывать любую функцию и обращаться к любому полю и не важно есть у этого объекта таклое поле или такая функция. Узнаем только на рантайме! Такой кусочек JavaScript'а в Kotlin'e --- class: compare-kotlin-to-js-v # Тип `dynamic` * .gray[ Доступен только в Kotlin JS ] * .gray[ Значение типа `dynamic` можно присваивать куда угодно ] * .gray[ В переменную типа `dynamic` можно писать что угодно ] * .gray[ Можно вызывать любую функцию и обращаться к любому полю ] * Операторные вызовы компилируются как есть ```kotlin d[0] = "a" d + a d > 1 ``` ```js d[0] = "a"; d + a; d > 1; ``` ??? * Операторные вызовы компилируются как есть. Найдите три отличия! --- # Тип `dynamic` * Доступен только в Kotlin JS * Значение типа `dynamic` можно присваивать куда угодно * В переменную типа `dynamic` можно писать что угодно * Можно вызывать любую функцию и обращаться к любому полю * Операторные вызовы компилируются как есть --- # Типизированные декларации * Ключевое слово: `external` * Опционально, аннотации: `JsName`, `JsModule`, `JsQualifier` * Из external декларации не порождается никакой код ??? Используя ключевое слово external и вспомогательные аннотации можно описать доступные на рантйме сущности в терминах котлина. Похожая возможнасть есть и в TypeScript. (Declaration File -- d.ts) И использовать их из кода на котлине. Ключевое слово external объязательно. Из external декларации не порождается никакой код. --- class: compare-js-to-kotlin-v # external свойства / переменные ```js var property = 1 ``` ??? Допустим у нас есть проперти в JS, который мы хотим считать целочисленным т.е. читать и писать только целые числа -- ```kotlin external var property: Int ``` ??? На котлине это будет вглядить так -- exclude: true ```typescript declare var property: number ``` ??? та же самая декларация на TS --- class: compare-js-to-kotlin-v # external функции ```js /* s {string} n {number} returns {string} */ function foo(s, n) { //... } ``` ??? Теперь, пусть у нас есть функция foo которая принимае строку и число, возвоащает строку. -- ```kotlin external fun foo( s: String, n: Double ): String ``` ??? Так будет выглядеть external декларация на котлине *** default vararg overload / union types --- # external интерфейсы и классы ```kotlin external interface A { val foo: Int fun bar(i: Int): Unit } external class B { val baz: Int fun boo(): Int } ``` ??? И по аналогии опишем external интерфейс и класс *** ограничения *** -- * `external interface` существует только на этапе компиляции ??? Важное замечание! В отличае от обычных интерфейсов `external interface` существует только на этапе компиляции. т.е. например их нельзя использовать для проверки типа --- exclude: true # Типизированные декларации: наследование --- exclude: true # Типизированные декларации: ... ??? JsName JsModule JsQualifier --- ## DefinitelyTyped* .footnote[\* [definitelytyped.org](http://definitelytyped.org)] -- * Большой репозиторий деклараций для TypeScript (>3000 деклараций) * Написанные вручную ??? Есть такой большое репозиторий написанных вручную деклараций для TypeScript >3000 деклараций Это стандарт де-факто в мире TypeScript да и не только Многие другие тулы, языки смотрят на эти декларации и завидуют :) -- ## А причем тут Kotlin? ??? А причем тут Kotlin? Да, мы в котлине тоже немного "завидуем" -- * Есть конвертор TypeScript деклараций в Kotlin (`ts2kt`) ??? И написали конвертор TypeScript деклараций в Kotlin Можно найти по называнию ts2kt в npm и на github -- * Планируем создать подобный репозиторий с декларациями для Kotlin ??? К сожалению автоматический тул не идеален да и сами декларации тоже Декларации написаны людьми, а не получились в результате компиляции, например из TypeScript, или путем вывода типа из JS исходников И содержат ошибки и не точности Поэтому мы планируем создать подобный репозиторий с декларациями для Kotlin --- # Вызов Kotlin кода из JavaScript ??? Теперь в обратную сторону. -- Полное имя любой декларации состоит из следующих частей: ??? Для того чтобы обратится к декларации полученной из Котлина необходимо знать его полное имя, оно состоит из нескольких частей -- * Ссылка на модуль ??? Во-первых это ссылка на модуль. Модульем может быть какой-то глобальный объект или ссылка на результат импорта с помощью какой-либо модульной системы. *** Js: JS module systems --- # Вызов Kotlin кода из JavaScript Полное имя любой декларации состоит из следующих частей: * Ссылка на модуль * Имена вложенных пакетов (package, namespace) ??? Далее идут имена вложенныт пакетов, namespace'ов -- ```kotlin package `foo.bar.baz` ... ``` ??? В начале любого файла можно написать пакет, этот путь нас и интересует --- class: compare-kotlin-to-js-v # Вызов Kotlin кода из JavaScript Полное имя любой декларации состоит из следующих частей: * Ссылка на модуль * Имена вложенных пакетов (package, namespace) * Сгенерированное имя декларации ??? Имена в котлене не всегда транслируются как есть и поэтому нужно знать какое имя получилось. Например -- ```kotlin fun `foo`() {...} ``` ```js function `foo`() {...} ``` ??? имя такой функции foo в JavaScript'е такое же -- ```kotlin fun `boo`(s: String) {...} ``` ```js function `boo_61zpoe$`(s) {...} ``` ??? А имя функции bar со строковым параметром уже не такое же --- # Вызов Kotlin кода из JavaScript Полное имя любой декларации состоит из следующих частей: * Ссылка на модуль * Имена вложенных пакетов (package, namespace) * Сгенерированное имя декларации ```
.
.
() ``` ??? итак, итоговое правило такое берем имя модуля точка путь к пакету точка и имя получившееся из нужной нам декларации Пример -- ```js someModule.foo.bar.baz.boo_61zpoe$("Hi!") ``` ??? Нормальное же имя? Или может стоит улучшить? --- # Улучшим API при помощи `JsName` ```js someModule.foo.bar.baz.boo_61zpoe$("Hi!") ``` -- .compare-kotlin-to-js-v[ ```kotlin `@JsName("boo")` fun boo(s: String) {...} ```] ??? Есть специальная анотаця JsName которое позволяет сказать компилиятору сгенерировать нужное нам имя Пишем на нашей функции JsName с параметро boo и оп --- # Улучшим API при помощи `JsName` ```js someModule.foo.bar.baz.`boo`("Hi!") ``` .compare-kotlin-to-js-v[ ```kotlin `@JsName("boo")` fun boo(s: String) {...} ``` ```js function `boo`(s) {...} // ``` ] ??? теперь в JavaScrip'е можно писать просто boo --- # Улучшим API при помощи `JsName` ```js someModule.foo.bar.baz.`abc`("Hi!") ``` .compare-kotlin-to-js-v[ ```kotlin @JsName(`"abc"`) fun boo(s: String) {...} ``` ```js function `abc`(s) {...} // ``` ] ??? По правде говоря, в качестве нового имени мы можем написать любой валидный идентификатор --- exclude: true # Особенности Kotlin JS (отличия от Kotlin JVM) --- # Инструменты ??? Поговорим про инструменты. -- * Intellij IDEA ??? Основой инструмент редактирования кода на котлине конечно же IDEA Есть комплишан, рефакторонги, инспекции и т.д. Есть и другие редакторы в которых есть поддержка котлина. Что-то официально, например, Eclipse Что-то нет, sublime, vim и т.д. -- * Инкрементальная компиляция (WIP) ??? Сейчас активно работаем над инкрементальной компиляцией -- * Sourcemaps ??? есть поддержка сорсмап -- * Оптимизатор (WIP) ??? К сожалению, существующие оптимизаторы для JavaScript'a оптимизируют наш код не так хорошо как нам хотелсь бы Поэтому мы сейчас работаем над небольшим оптимзатором который заточен под код генерируемый нашим компилятором, Но это не замена существующих оптимизаторов, а только помощник. -- * Конвертор TypeScript деклараций в Kotlin (ts2kt) ??? И конвертор про который я уже говорил *** TODO Важный аспект: язык разрабатывается c оглядкой на tooling --- # Инструменты сборки * gradle * kotlin-frontend-plugin * maven * ant * webpack (через kotlin-frontend-plugin) ??? На данным момент мы предлагаем следующие инструменты для сборки --- exclude: true # Multiplatform projects --- exclude: true # Планы * Оптимизация компилятора и плагина * Multi-Platform projects * Новая инфраструктура в компиляторе ??? Сейчас и в блажайшее воремя тратим много времени на оптимизации в компиляторе и плагине Multi-Platform projects являются для нас важным Большая фича котору Ну и между дел *** про Kotlin и Kotlin JS Multiplatform projects tooling & UX IR ES X d.ts? больше оптимизаций??? --- # Планы Kotlin JS * Инкрементальная компиляция ??? Мы активно работаем над инкрементальной компиляцией для Kotlin JS -- * Unit testing ??? Так же работаем над поддержкой различных тестовых фреймворков -- * Оптимизация размера и скорости ??? Работаем над уменьшением размер получаемого кода и не забываем про его скорость -- * Поддержка новых версий ECMAScript ??? Планируем в будущем поддержать новые версии ECMAScript как compilation target -- * WebAssembly (?) ??? Так же краем глаза посмотриваем на WebAssembly --- exclude: true Зачем мне все эти заморочки если есть Х? ??? Работа с популярными JS библиотеками (React и т.п., живой пример?) DSL, Type safe HTML builders (см пример для react) --- # Полезные ссылки * Сайт Kotlin: [kotlinlang.org](https://kotlinlang.org) * документация: [kotlinlang.org/docs/reference/](http://kotlinlang.org/docs/reference/) * Try Kotlin: [try.kotl.in](https://try.kotlinlang.org) * Kotlin Koans: [try.kotl.in/koans](http://try.kotlinlang.org/koans) * Slack: [kotlinlang.slack.com](https://kotlinlang.slack.com) * регистрация [slack.kotl.in](http://slack.kotl.in) * канал про Kotlin JS: [#javascript](https://kotlinlang.slack.com/messages/C0B8L3U69/) ??? Набор полезных ссылок, в конце будет ссылка на слайды С вопросами и предложениями приходите к нам в Slack, в канал #javascript --- class: center middle # Спасибо! --- class: center middle ### Залим Башоров Kotlin ([kotl.in](https://kotlinlang.org)) zalim.bashorov@jetbrains.com .twi[[@bashorov](https://twitter.com/bashorov)] .link-to-slides[ Ссылка на слайды: [zal.im/slides/holyjs17](http://zal.im/slides/holyjs17) ] .link-to-slides[ Временная ссылка на слайды: [zal.im/s](http://zal.im/slides/holyjs17) ] ??? ; ?. !! ?: new ranges outside loops Basic Types Nullable? Explicit Conversions Operations pro: Extension functions coroutines listOf и т.п. data class sield classes nested/inner classes Enum Object delegation properties destructuring in lambda smart cast //// пока ES5, будет больше про то что во что компилируется??? Representing Kotlin types in JavaScript reflection ссылку на документацию? для каждой фичи правильное название для фичи для гугленя? koans