Architec.Ton is a ecosystem on the TON chain with non-custodial wallet, swap, apps catalog and launchpad.
Main app: @architec_ton_bot
Our Chat: @architec_ton
EU Channel: @architecton_eu
Twitter: x.com/architec_ton
Support: @architecton_support
Last updated 2 weeks, 2 days ago
Канал для поиска исполнителей для разных задач и организации мини конкурсов
Last updated 1 month ago
В общем, я ВНЕЗАПНО понял, что уже вообще-то почти декабрь, а значить вот-вот начнётся новый Advent of Code. Забираю обратно все свои подтрунивания над коммунальными службами, которые "не были готовы к зиме". Я тоже не был готов.
Для новых подписчиков – я уже третий год организую "клуб решал Advent of Code" и мы 25 дней решаем задачи и ~~страдаем~~ получаем положительные эмоции. В этом году организацию начал очень поздно, так что скорее всего состав решающих будет достаточно камерный :)
Заходите в канал @aoc_club и зовите друзей!
Где-то лет пять я пользуюсь fixup'ом через IDE, чтобы добавлять изменения к коммитам из истории, и за это время смирился, что эта фича работает странно. Она просто создаёт коммит, но не ребейзит его автоматически, и приходится через интерактивный ребейз двигать коммит и делать fixup руками. Даже в консольном гите удобнее, там можно при ребейзе указать флаг \-\-autosquash
и все коммиты с префиксом fixup!
или squash!
сами посквошатся как надо. Ну то есть явно какой-то баг в IDE, иначе кнопка "Fixup" почти ничем не отличается от "Interactively Rebase from Here".
И вот спустя годы, когда в очередной раз пригорело от поведения fixup, я решил найти этот баг в YouTrack чтобы влепить звезду. Нашел. И оказалось, что можно было не страдать. Просто IDE учитывает настройку гита rebase.autosquash
и если её выставить в true
, всё начинает работать как надо ?
В комментариях к issue можно наблюдать знатное полыхание на тему "а как я об этом должен был узнать?" и я полностью согласен.
Есть ещё одна ловушка с этой фичей. При коммите нужно обязательно нажать "Commit and Rebase", который спрятан внутри выпадающего списка около кнопки "Commit", иначе магии не произойдёт. Про это, кстати, есть отдельная issue.
? TL;DR
Чтобы в IDE не двигать руками коммиты с префиксами fixup!
и squash!
в нужное место при ребейзе, включи настройку rebase.autosquash
:
git config \-\-global rebase.autosquash true
Продолжаем тренировать #насмотренность с inline-классами. В предыдущем посте я обещал рассказать про boxing inline-классов. Но сначала немного поговорим про примитивы.
Один из моих любимых вопросов на собеседовании — есть ли в Kotlin примитивы? Вот в Java есть примитив int
и объект Integer
, а в Kotlin только Int
. На самом деле при компиляции под JVM компилятор сам решает что использовать. Когда возможно, использует примитив, но если тип используется в дженерике или объявлен как nullable, нужен объект и происходит boxing — примитив оборачивается в объект Integer
.
Теперь вернёмся к inline-классам. Подобно примитивам, при работе с inline-классами Kotlin старается избежать лишних обёрток. Чтобы убедиться, что в конкретном случае не происходит boxing, можно посмотреть как код выглядит для JVM через Show Kotlin Bytecode > Decompile
. Случаи, когда происходит boxing, подробно описаны в KEEP. Рекомендую прочитать полностью, а пока сосредоточимся на самых интересных моментах.
*0️⃣ Нуллабельность*
Если нужна нуллабельность, но хочется обойтись без boxing'а, можно создать специальное значение, которое будет заменять null
. Помните Color.Unspecified
в Compose? Это как раз оно. Кстати, недавно подобные специальные значения добавили и inline-классам входящим в состав TextStyle
. В сообщении к коммиту есть бенчмарки показывающие сколько аллокаций удалось на этом сэкономить.
*1️⃣ Создание подтипов*
Когда в Kotlin появились sealed-интерфейсы, я подумал что они будут неплохо комбинироваться с inline-классами. Была мысль завести абстракцию чтобы одинаково работать со строками и текстовыми ресурсами:
```
sealed interface TextValue {
fun get(resources: Respurces): String
@JvmInline
value class Plain(val value: String) : TextValue {
fun get(resources: Resources) = value
}
@JvmInline
value class Res(@StringRes val resId: Int) : TextValue {
fun get(resources: Resources) = resources.getString(resId)
}
}
```
Проблема в том, что тут от inline-классов нет никакого толка. Мы будем использовать интерфейс, а не конкретный inline-класс, так что boxing будет происходить всегда. Компилятор не знает какую именно реализацию интерфейса мы подложим, а значит не может заменить интерфейс на внутренний тип.
Вариантов минимизации boxing'а в подобных случаях как минимум два:
☝️ Если тип внутреннего значения у разных "подтипов" разный, можно хранить общий супертип. Так сделано в Result, внутри лежит Any?
, а чтобы отличать контент от ошибки, создан вспомогательный тип Failure, в который оборачиваются ошибки.
✌️ Можно комбинировать тип и значение. Это хорошо работает с примитивами. Например, в Compose TextUnit
может иметь размерность Sp
и Em
, но это всё один inline-класс. Информация о типе хранится в младших битах Long'а, а значение в следующих за ними битах. Другой пример такого подхода — Duration из stdlib.
*2️⃣ Дженерики*
С использованием inline-класса на позиции дженерика всё понятно — в этом случае без boxing'а не обойтись. Но дженерики указанные у самого inline-класса не влияют на boxing.
Классный пример применения inline-классов с дженериками — безопасные ID. Представим, что у нас есть несколько разных сущностей со строковыми ID и мы хотим чтобы на уровне контракта нельзя было по ID пользователя запросить товар и наоборот. В 2019 году Jake Wharton предложил создавать inline-классы для строгой типизации ID. С тех пор появилась возможность указывать дженерики у inline-классов и теперь можно не плодить отдельные классы-обёртки на каждый тип сущности, достаточно создать один inline-класс с дженериком:
```
@JvmInline
value class Id(val value: String)
data class User(val id: Id)
```
Всем inline-классы в код!
UPD: @senk0n подсказывает, что Romain Guy недавно написал пример как можно в inline-класс запихнуть целую сетку 8х8, каждая ячейка которой может иметь значение 1
или 0
.
Architec.Ton is a ecosystem on the TON chain with non-custodial wallet, swap, apps catalog and launchpad.
Main app: @architec_ton_bot
Our Chat: @architec_ton
EU Channel: @architecton_eu
Twitter: x.com/architec_ton
Support: @architecton_support
Last updated 2 weeks, 2 days ago
Канал для поиска исполнителей для разных задач и организации мини конкурсов
Last updated 1 month ago