Минутка просвещения

Читать в телеге. Когда-то там были посты не только от меня.

Ковариантность и контравариантность

У меня всегда были трудности с запоминанием умных терминов, хотя суть вариантности довольно проста. Если иерархия типов сохраняется в том же порядке для производных типов (которые используют искомый тип как параметр), то это (ко)вариатность. Если идет в обратном порядке — (контра)вариантность. Если нет иерархии — (ин)вариантность.

Если термины из теории категорий — “сложна”, то можно мыслить в терминах потребитель/производитель, в Kotlin и C# параметры так пишут — in и out вместо всяких плюс-минусов, и это гораздо читаемее.

СсылкаКомментировать

Вариации сна для компьютера

  1. Бездействие (aka S1, idle) — CPU остановлен, система потребляет чуть меньше энергии: почти все устройства переключены в режим низкого энергопотребления или выключаются (ЖД, подсветка экрана).
  2. Сон (aka S2) — как S1, только CPU отключен от питания (разница с S1 минимальна, на некоторых материнских платах его даже не реализовывают).
  3. Ждущий режим (aka S3, sleep, suspend) — почти все устройства, кроме RAM отключены, состояние хранится в памяти. RAM обновляется медленнее. Жрет питание/батарею, но чуть-чуть.
  4. Гибернация (aka S4, hibernate) — состояние сохраняется на диск (например, в swap), питание полностью отключается. Однако не все оборудование корректно может восстановить свое состояние, поэтому этот режим обычно отключен по умолчанию.
  5. Мягкое выключение (aka S5, soft off) — все выключено, но некоторые порты/контроллеры включены (чтобы включить комп по LAN или по нажатию кнопки на клавиатуре, например).
  6. Гибридная гибернация (aka hybrid suspend) — состояние сохраняется на диск, как в гибернации, а потом комп переводится в ждущий режим. Позволяет быстрее выходить из сна, но не терять состояние, если питание пропадет. Есть еще вариация, когда сначала выполняется suspend, а потом, по таймеру или по триггеру (например, низкий заряд батареи) кратенько просыпается и уходит в гибернацию.
  7. Modern Standby (aka S0 low power idle, S0ix) и PowerNap — маркетинговые названия для S0 (полностью работающий комп), просто часть функций системы не работают и часть периферии отключена. Но при этом выполняются фоновые задачи и остается подключение к сети (“как у смартфона”). Питание потребляет много, и ноутбук в таком режиме в сумку класть не стоит — может перегреться.
СсылкаКомментировать

Псевдонимы для типов и value-классы

Почти бесплатно повысить читаемость кода можно за счет псевдонимов типов (type aliases). Например, можно заменить в бизнес-логике безликий UUID на UserId. Есть это почти во всех языках: typedef или даже #define в С, using в C++ и C#, type alias в Kotlin, type в Scala/Haskell и даже в питоне, но не в Java. Можно еще использовать локально: импортировать тип с другим именем. Но у псевдонимов есть банальный недостаток: ничто не помешает в UserId запихать другой UUID, например ObjectId.

Чтобы компилятор проверял подобные несоответствия, можно использовать value-типы (они же Single case discriminated union). Обычно это класс-обертка над целевым типом, который имеет единственное поле и при возможности заменяется после компиляции на целевой тип. Например, в Kotlin это будет

@JvmInline
value class UserId(val value: UUID)

В некоторых языках еще можно добавить логику (например, проверку в init блоке или добавить методы-геттеры). Если value-типов нет, можно достичь примерно того же, используя data-классы с одним полем (а где-то может и вообще разницы не будет).

Очевидная проблема оберток в том, что к содержимому всегда придется обращаться через поле или паттерн-матчинг, а не напрямую (и какой-нибудь делегацией это, увы, не решается). А из-за потенциального оборачивания страдают библиотеки, использующие рефлексию, например, сериализация в JSON. В Scala это решили с помощью opaque-типов: рядом с объявлением класса он работает прозрачно, как type alias, а снаружи — непрозрачно, и доступа к целевому типу нет. При этом там есть и “обычные” value-классы, которые почти такие же, как в Kotlin.

А еще и в Scala и в Kotlin ждут Project Valhalla, чтобы можно было создавать пользовательские типы, доступ к которым осуществляется по значению, а не по ссылке. В комбинации с inline это позволит решить часть проблем с оборачиванием value-типов.

СсылкаКомментировать

Help me, step-container, I'm stuck!

Иногда контейнер не может подняться, а в логах полезной информации нет. Надо бы провалиться в консоль контейнера, чтобы проверить все изнутри, но из-за постоянных рестартов это не получится сделать. В этом случае можно сохранить текущее состояние контейнера:

docker commit %container_id% %some_name%

и стартовать его с командной строкой:

docker run -it %some_name% /bin/sh

А потом уже дебажить, что там внутри не так.

СсылкаКомментировать

Из-за чего команда профессионалов может работать фигово

В продолжение заметки про продуктивность — а почему в команде из кучи замотивированных сеньоров все равно может получиться что-то не то?

Причин может может быть масса [1, 2, 3]:

  1. Они делают не то, что важно (в т.ч. из-за того, что увлекаются чем-то интересным для себя), часто из-за отсутствия четких целей, отсутствует фокус или пытаются сделать все и сразу.
  2. Они ждут другую команду (из-за плохого разделения, из-за мутных процессов или из-за чрезмерной специализации, когда не можешь помочь другой команде).
  3. Нет четкого понимания, что нужно сделать, тратится время на выяснение этого (хорошо еще, если до разработки, плохо, если на миллионе встреч).
  4. Нет четких зон ответственности и ролей — непонятно, кто должен что сделать, чаще всего превращается в “не делает никто”. В том числе когда что-то можно было предотвратить малой кровью вместо героического расхлебывания последствий.
  5. Они не умеют говорить “нет”.
  6. Нет долговременного планирования, слишком большая сосредоточенность на скорости, а не качестве.
  7. Они плохо понимают предметную область и конечного потребителя.
  8. Нет признания результатов, чувства достижения, мотивация делать нормально падает, появляется куча новых проблем.
СсылкаКомментировать

Визуализация explain для SQL-запроса и рекомендации по индексам

Хороший пост, в котором как для даунов объяснены основные типы индексов, даны советы, когда какие использовать, и приведены примеры запросов, которые можно оптимизировать. А еще там есть ссылка на сервис, в котором можно визуализировать выполнение запроса и получить рекомендации, какие индексы добавить.

СсылкаКомментировать

Docker на Маке

brew install docker поставит только клиенты докера, без демона. docker-machine, который запускает докер в виртуалке (sic!), уже устарел и его хрен запустишь. Docker Desktop платный (ну, на честном слове платный, но все же) и требует GUI…

В итоге рабочий вариантом оказалась… установка кубера, а именно minikube. Можно еще туннелирование в локальную сеть настроить или поиграться с kubectl. Но после таких приколов хотя бы понимаешь, почему некоторые считают, что надо минимум 32 гига оперативки для разработки на маке :/

СсылкаКомментировать

Даты изобретения сортировок и кроссбаузерность

После изучения всяких фронтенд штук захотелось сделать что-нибудь очень простое. Прям наговнякать. Решил сделать временную шкалу с датами изобретения алгоритмов сортировки на CSS, и собирать HTML на голом питоне.

Во-первых, я все еще офигеваю, как сложно сделать какие-то элементарные вещи. Во-вторых, что до сих пор в трех разных браузерах одно и то же форматируется по-разному, хотя я использую все из древних стандартов. Я честно пытался написать кросс-браузерно, но в итоге мне это надоело и я сделал браузеро-зависимый CSS на основе всяких трюков. Отправляйте пулл-реквест, если умеете верстать или хотите попытать силы :)

Наконец, самой тяжелой частью оказались собственно данные. Нашел статью, где вроде есть все, что нужно, но качество данных там просто впечатляющее. Например, в качестве авторов Stooge Sort указаны “Prof. Howard Fine and Howard”. А на самом деле она фигурирует в упражнении из Кормена: “Professors Howard, Fine, and Howard have proposed the following “elegant” sorting algorithm…” — и является отсылкой на комедийный коллектив. Изначально в планах было найти оригинальные источники для каждой сортировки, но это оказалось нетривиальной задачей, поэтому я отложил это до лучших времен.

Демка тут.

СсылкаКомментировать

Статическая типизация — не панацея

Что выведет этот кусочек кода на Kotlin?

fun main() {
    data class SomeClass(val someValue: Int)
    val someNull = null

    println(someNull + SomeClass(12))
}
Ответ

Выведет nullSomeClass(someValue=12). Почти как в JS:)

Все благодаря выводу типов: компилятор находит расширение operator fun String?.plus(other: Any?), вызывает у обоих аргументов toString() и склеивает их.

Хорошо хоть, узнал это не из продакшен-кода, а из познавательного видео, в котором мужик рассказывает про “переопределение” оператора сложения для nullable типов.

СсылкаКомментировать

Экономьте газ

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

Однако есть ниша, в которой экономить элементарные операции все еще актуально — смарт-контракты. Каждое вычисление чего-то стоит. Например, в Etherium каждая операция смарт-контракта тратит немного “газа”, не уложился в лимит — транзакция откатывается. В периоды большой нагрузки на сеть газ стоит дороже, в периоды низкой, соответственно, дешевле. Если алгоритм оптимизирован, то на его выполнение будет потрачено меньше газа и, как следствие, меньше криптовалюты.

Хотя некоторые паттерны неоптимального кода довольно тривиально обнаруживаются и исправляются, и вызваны скорее незрелостью технологии (например, расчет суммы, которая не зависит от внешних параметров, может быть заменен формулой). Есть несколько инструментов для оценки использования газа, а еще можно покрыть контракт тестами (например, с помощью hardhat или brownie) и выводить либо оценку, либо точное значение потраченного газа. Но чтобы оптимизировать — тут головой еще надо думать, увы.

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

СсылкаКомментировать