Telegram Web Link
Копаясь в скала-коде восьмилетней выдержки, узнал, как раньше скалисты парсили json. Был в стандартной библиотеке модуль parser-combinators для описания разного рода грамматик. Ну а в нём, соответственно, готовый парсер json.

Как можно представить json в языке с сильной системой типов и мощным компилятором? Через ADT, покрывающее типами все возможные варианты значений? Можно, но есть вариант попроще — Map[String, Any]. А массив джсонов — List[Any].

Ну и функция для парсинга, возвращающая Option[Any], где в Any может быть либо Map[String, Any], либо List[Any] 😊. Не смотрел по истории коммитов, был ли уже в стандартной библиотеке на момент написания этого кода тип Either, но вряд ли он бы сильно помог. Короче, такое вот тайплевельное программирование на максимуме, это вам не в circe кодеки макросами выводить.

Сейчас всё это дело удалено из стд, задепрекейчено и остаётся висеть на гитхабе артефактом древних времён https://github.com/scala/scala-parser-combinators/tree/1.1.x
Ехал Any через Any
Вчера вышел релиз ZIO 1.0, и по этому поводу Дегуз написал большой блогпост про разработку библиотеки от прототипа до консалтинговой компании. История подробная, с упоминанием основных технических решений, их мотивации, и людей, работающих над ними. Редко получается проследить историю развития софта в живом рассказе, а не по коммитам.

https://ziverge.com/blog/zio-1.0/
Я сделал ещё один канал, на который буду сбрасывать понравившуюся мне музыку. Преимущественно электронная сцена. Существенную часть этой музыки я слушаю во время работы, так что может быть другим программистам она тоже будет интересна.

Милости прошу к своей кибердиджейке — @choreus
Нашёл маленький, едва заметный шанс на то, что в третьей скале исправят одну из самых раздражающих меня штук:

Option(42).fold(Nil)(x => List(x, x, x)) // not compiles


Тут компилятор выводит тип результата по первому списку аргументов как Nil, и не может догадаться, что List[Int] является его супертипом. В итоге приходится писать тип руками

Option(42).fold(List.empty[Int])(x => List(x, x, x))


Удручает то, что в пулл-реквесте с исправлением не было коммитов с мая, но вдруг случится чудо https://github.com/lampepfl/dotty/pull/9076

Ещё там подробно и простым языком расписано решение, так что можно понять, что происходит, не разбираясь в кишках компилятора.
Забавные коаны про гит. О консистентности комманд прямо в точку

https://stevelosh.com/blog/2013/04/git-koans
Не люблю зашитые в код недетерменированные функции, например, получение текущего времени. В передовых методиках функционального программирования можно заинъектить часы в код через F[_]: Clock или ZIO[Clock, E, A], но даже без них гораздо лучше сделать

trait Clock {
def nanoTime(): Long
}


и прокидывать в свои классы его, а не хардкодить там System.nanoTime.

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

Аналогично для всяких рандомов, гуидов и прочего.
Ух ты, что есть — анимированная документация по основным комбинаторам ZIO

https://zio.surge.sh/

Покрыты пока далеко не все фичи, но особенно на Schedule выглядит очень наглядно
Очень подробный рассказ про конкарренси под капотом у скаловых монадок. Понятно расписано, как работают легковесные потоки (файберы) поверх jvm-тредпулов, сами тредпулы, и как всё это взаимодействует с нашими цепочками .flatMap. Более цельной и детальной информации на эту тему пока не видел, автор определённо заслуживает звёздочки на гитхабе 😉

https://github.com/slouc/concurrency-in-scala-with-ce
Долгий интересный разговор про использование ReasonML в продакшене, и почему не тайпскрипт (потому что ансаунд). ResonML — это попытка фейсбука адаптировать OCaml под нужды фронтенда. Синтаксис, конечно, испортили скобочками, но в остальном это тот же самый окамл.

При попытках работать с тайпскриптом меня удручала его «особенность» компилировать код, в котором типы на самом деле не сходились. На втором месте по уровню раздражения была необходимость постоянно помогать компилятору в местах, где он не может вывести типы. Ризон этих проблем лишён, но количество программистов на рынке, конечно, хех мда.

https://www.youtube.com/watch?v=nZDN6XCM1X0
Alexandru Nedelcu опубликовал блогпост с размышлениями о flow-sensitive typing на примере старого-доброго Option#get из скалы. Если вкратце, flow-sensitive typing — это когда

if (option.nonEmpty) {
println(option.get) // ← проходит проверку компилятора
} else {
println(option.get) // ← упадёт с ошибкой
}


Такое активно используется в тайпскрипте и котлине.

И что могу сказать: ДА, ОЧЕНЬ ХОЧЕТСЯ. Казалось бы, уже в 2.13 появились literal types, а в третьей скале будут дизъюнкции типов. Поэтому есть надежда, что через какое-то время отпадёт необходимость запрещать Option#get в линтерах.

https://alexn.org/blog/2020/11/12/i-like-option-get.html
Роб Норрис опубликовал маленькую библиотечку, которая умеет получать название типа в рантайме. Такая легковесная альтернатива TypeTag для логирования типа выражения.

@ import org.tpolecat.typename._
import org.tpolecat.typename._

@ def log[T](expr: T)(implicit typeName: TypeName[T]): String = s"expression of type ${typeName.value} with value ${expr}"
defined function log

@ log(List(1, 2, 3))
res3: String = "expression of type List[Int] with value List(1, 2, 3)"


Тем более библиотека состоит из макроса на три строчки, который можно просто стащить себе в проект.

https://github.com/tpolecat/typename

И, раз такое дело, грех не вспомнить библиотеку sourcecode, которой очень удобно вытаскивать названия функций и автоматически подставлять их в названия метрик.
Увидел в канале про Rust (кстати, рекомендую) пост о том, как сделать паттерн-матчинг строк с выделением их составных частей. Например, разматчить строку по известному префиксу: https://www.tg-me.com/dereference_pointer_there/1366

Задумался о том, как написать то же самое в скале. Оказалось, что с 2.13 уже и писать ничего не надо: в стандартной библиотеке есть решение для матчинга не просто префиксов, а любых частей интерполированных строк!

"I love Scala" match {
case s"I $feeling $language" => Map(language -> feeling)
case other => Map.empty
}

Работает эта радость за линейное время по вот такому алгоритму: https://research.swtch.com/glob
Параллельная компиляция и диаграммы билдов в новой версии скала-плагина для IDEA — огонь. Выражаю признательность разработчикам JetBrains.
Авито уже два года держит на гитхабе и актуализирует описание своей организационной структуры. Там всё: грейды разработчиков и руководителей, структура команд, perfomance review.

Из того, что было наиболее интересно:
Архитектурный комитет. Дизайн-ревью проектов принято проводить не только внутри команды, но и с привлечением экспертов из других команд. По идее это помогает избежать взращивания внутри команды несогласованных с остальной компанией подходов. И сразу же получить взгляд со стороны внешних сервисов, которые могут быть негативно затронуты новым решением;
→ Исследованием инцидентов и управлением работой над ошибками занимаются QA, а не разработчики или SRE. А ведь действительно логично, что аварии в продукте не проходят мимо ответственных за качество продукта 🙂
→ Сформулировали самое важное требование для джуна, которое никак не могло кристаллизоваться в моей голове — «Не повторяет одинаковых ошибок»;
→ Инженерное визионерство прописано в требованиях только для Lead-разработчика, для Senior — только проактивное исправление проблем в текущей архитектуре. В моих влажных мечтах эти требования сдвинуты на грейд ниже: визионером должен быть уже Senior, а зоркий глаз на проблемы в проекте и миддлам неплохо бы иметь;
→ Много внимания уделено доведению глобальных задач. Уже джун должен понимать, как его задачи в спринте связанны с целями компании на квартал. А сеньор должен участвовать в постановке таких целей.

Я почитал с огромным удовольствием: https://github.com/avito-tech/playbook
Гетерогенные списки из коробки в Scala 3!

Как обычно позже всех узнал, что помимо прочих фич для тайп-левельного программирования, в стандартной библиотеке скалки основательно переделали тюплы. Завезли множество полезных операций, для которых в Scala 2 надо тащить в зависимости shapeless и использовать HList.

Для тюплов будут доступны почти все операции над коллекциями, например, конкатенация, взятие элемента по индексу, filter, map. А компилятор при этом любовно выведет типы, проверит, что нет index out of bounds. Ну не красота же?

Накидал несколько примеров использования новых фич. map , принимающая полиморфную функцию, и реализованная через матч-тайпы, выглядит, конечно, космически.

Внутри это всё построено на механизме Match Types, а узнал я об этом из блогпоста с программированием списка длинны, известной во время компиляции.
Lil Functor
Ух ты, что есть — анимированная документация по основным комбинаторам ZIO https://zio.surge.sh/ Покрыты пока далеко не все фичи, но особенно на Schedule выглядит очень наглядно
Человек, который сделал анимированную документацию по ZIO, написал вот ещё библиотеку на макросах для полуавтоматического вайринга зависимостей: достаточно перечислить нужные компоненты без ручного указания горизонтальных/вертикальных связей.

Самое крутое там — это понятная ошибка компиляции. То, что вываливает компилятор при ручном вайринге ZLayer, у меня вызывает флешбэки со стенами нечитаемых ошибок от компилятора C++

https://github.com/kitlangton/zio-magic

И раз уж речь зашла о проблемах ZIO-экосистемы... Библиотека для моков в zio-test до сих пор не умеет без костылей проверять, что метод ни разу не был вызван 🤦‍♂️
2025/07/09 09:37:07
Back to Top
HTML Embed Code: