Lil Functor


Channel's geo and language: Russia, Russian
Category: Technologies


Pure functional and composable channel
Чат: https://t.me/+L-xb_m_4lnY3Y2Fi

Related channels

Channel's geo and language
Russia, Russian
Statistics
Posts filter


Видео моего доклада «Как в Scala переложить JSON?»

https://youtu.be/usp3Y0-VxwM?si=XwuewnRQt-Nj6Bfm

Кроме него рекомендую посмотреть доклад Андрея с того же митапа. Андрей руководит командой наших базовых сервисов и рассказал не очередную фантазию о связи ФП и математики, а суровую жизнь разработчика core-компонент. Историю инцидентов в нашей системе авторизации, с которыми мы боролись весь прошлый год, планомерно повышая её отказоустойчивость. Видео доклада Андрея: https://youtu.be/JORwagh4lzM?si=Wlhe_VA6iUUYDNsg

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


Как в Scala переложить JSON?

В эту субботу буду выступать в Москве на Scala-митапе от Яндекса. Приходите, пообщаемся: https://events.yandex.ru/events/vertech/scala-meetup. Вход бесплатный по регистрации.

Подниму животрепещущую тему, волнующую каждого программиста: как переложить эти проклятые json-ы? Почему они сразу лежат не там где надо? И конечно, будет немного праздных разговоров о ФП и математике.


Конференция F[Scala] 🪜

Всем привет!
Уже в следующую субботу в Москве пройдёт конференция по Scala и функциональному программированию, которую мы организуем с коллегами.

28-е октября, один из московских офисов Яндекса, бесплатная регистрация по ссылке:
https://events.yandex.ru/events/vertech/fscala

Конференций по Scala и ФП у нас не было уже очень долго. Я по ним изрядно соскучился, хотя уже давно почти не программирую на работе. Есть в таких конференциях особый шарм, совмещающий прагматику и эстетство, бизнес-задачи и академию. Это стоит того, чтобы впервые попробовать себя в роли организатора.

У нас будет шесть докладчиков, некоторые из которых уже имеют некоторую известность, а некоторые станут открытием для сообщества. Приходите, конфа бесплатная. Кого знаю — пообщаемся, кого не знаю — займёмся т.н. «нетворкингом». Увидимся 28-го октября!


Kit Langton, автор макроса для автовайринга ZLayer, отметился в любопытном пропозале на форуме разработчиков компилятора Scala. В пропозале обсуждается синтаксический сахар для аппликативной композиции по аналогии с for-comprehension для монадок. Если сильно упрощать смысл аппликативных вычислений до наиболее частого практического применения — это вычисление нескольких эффектов параллельно и сбор результатов в одном месте (академики, не ругайтесь).

Сейчас в популярных библиотеках параллельные вычисления описываются вспомогательными функциями, и это не особо удобно в реальности, когда собрать надо с десяток значений:

ZIO.mapParN(fetchName, fetchAge)((name, age) => User(name, age))

А программисты хотят синтаксического сахарка, как если бы вычисления были последовательными:

afor {
name


sbt версий 1.6.0 и 1.6.1 был с багом: при ошибке инициализации фреймворка для юнит тестов он успешно завершал билд. Это случайно заметили на коммьюнити-билде Scala 3: там у десятка проектов не отрабатывала даже компиляция тестов, а билды были зелёными.

Такие молчаливые баги я не люблю больше всех. Догадаться, что сломался CI при зелёных чеках, можно разве что по подозрительно быстрому времени завершения. Да и то, пока кто-то на него посмотрит, уже случится пара (десятков) выкладок в прод.

Пофикшено в 1.6.2.


Uber публично задокументировал свой подход к организации микросервисов — Domain-Oriented Microservice Architecture (DOMA). Он решает проблему колоссального возрастания сложности системы у организаций с несколькими тысячями микросервисов. Для организаций меньшего масштаба весь подход будет избыточен, а вот идею «слоёного дизайна» стоит почерпнуть.

Принцип заключается в разделении системы на иерархию слоёв. В самом низу лежат инфраструктурные сервисы, наверху — реализация продуктовых фич и представления данных для фронтенда. Прямые зависимости между сервисами допустимы только внутри слоя, а в соседний слой можно сходить через единую точку входа. Причём соседний — это нижний. Слой может зависеть только от слоя, который находится под ним.

Например, продукт с оплатой услуг раскладывается на три слоя (сетевую инфраструктуру и гейтвеи фронтенда для простоты отброшу):

* Биллинговая система, оперирующая платёжками, реквизитами юр. лиц, данными банковских карт, чеками и тому подобным, на нижнем слое иерархии. Она может быть использована в принципиально разных продуктах, главное, чтобы они забирали деньги у пользователей;
* Сервисы ценообразования и механики применения услуг в конкретных продуктовых доменах. На этом слое может быть несколько сервисов, если у направлений бизнеса отличается логика тарификации;
* Продуктовые сервисы, реализующие юзер-стори.

Отчётливо видно движение от универсальной функциональности к специфичной и от высокой критичности к низкой. Развалится биллинг — пострадают все, а если продуктовый сервис — только его фича.

Когда пользователь активирует платную услугу, продуктовый слой обращается только к соседу ниже запросами вида «активируй услугу X пользователю Y». Он не работает с биллингом и тем более не реализует бизнес-логику в терминах биллинговых систем. Нижний слой тоже не обращается к продуктовому и не знает его моделей.

Важно не использование «слоёв» в каноничном уберовском понимании, а наличие иерархии в организации системы. Без неё сложность растёт неконтролируемо: домены переплетаются, экспертиза размазывается между командами, граф зависимостей сервисов превращается в ёжика. Выделить правильную иерархию без лишних абстракций — сложная архитектурная задача, но даже несовершенная иерархия лучше её отсутствия.


C4 Model

Меня сильно заинтересовали способы грамотной визуализации архитектуры программных систем, потому что диаграмы уровня [kafka] → [scala] → [postgresql] порядком поднадоели. Хочется выработать максимально информативный, но в тоже время удобный способ рисовать то, что мы проектируем.

Из того, что нашёл, больше всего понравился фреймворк C4model. Но зачем вообще нужен целый фреймворк для визуализации? Без него каждый раз приходится импровизировать, что нарисовать на диаграмме. А нарисовать хочется многое: юзер-стори, технологии, карту микросервисов и хранилищ, технические ограничения... В итоге получается либо картинка с хаотичным набором стрелочек и квадратиков с перемешанными абстракциями «всё в одном», либо фрустрация и рисование примитивной схемы а-ля «ну тут сервис, тут база, всё понятно вроде».

C4 предлагает решение через разделение уровней детализации: нарисовать несколько схем от взляда на систему с высоты птичьего полёта до всё более подробных представлений отдельных процессов. Cлоёв ровно четыре, и их описание можно прочитать на сайте фреймворка. У себя на сайте я оставил краткий конспект (тут и так слишком много слов для телеграма).

Мне нравится подход C4, но с парой ремарок:

→ Я бы сократил C4 до C3. Визуализация связей внутри кода приложения кажется избыточной: если все сервисы пишутся по единым паттернам в неком подобии гексагональной архитектуры, то и связи между модулями будут типовыми. Лучше инвестировать в выработку организационных стандартов кодирования, чем в рисование/генерацию UML.
→ В микросервисных реалиях одно приложение содержит в себе совсем немного компонент, поэтому на третьем уровне вместо статической диаграммы внутренностей отдельного приложения лучше нарисовать основные процессы, которые оно выполняет. Для этого хорошо подходит диаграмма последовательности из PlantUML.
→ Описание слоёв не является догмой и должно адаптироваться под нужды организации или команды.

Напишите в комментарии, что вы у себя используете для визуализации/документирования разработок?


Дорогие подписчики, поздравляю с новым годом!

Желаю вам в 2022 карьерных достижений, академических изысканий и жадного ума 🚀


Текстовая версия недавнего доклада Li Haoyi (автора книги Hands On Scala и множества библиотек и инструментов) о применении Scala в DataBricks. Наконец-то нормальная саццесс-стори вместо набивших оскомину драм в сообществе. Там и про управление зависимостями, и про сборку с CI, и про скрипты на библиотеках самого Li.

https://databricks.com/blog/2021/12/03/scala-at-scale-at-databricks.html

we have been able to scale our Scala-using engineering teams without issue and reap the benefits of using Scala as a lingua franca across the organization


Вот и пригодилась компиляция Scala → JavaScript. Только не для фронтенда.

Daniel Spiewak в концепте serverless-фреймворка предлагает компилировать код на Scala в JS, чтобы запускать его на движке V8. Таким образом получится избежать проблемы холодного старта JVM для короткоживущих функций. V8 не надо прогревать, а код для него можно писать всё на той же Scala. Не зря ведь scala.js делали и библиотеки под него кроссбилдили!

Понятно, что scala-native слишком далека от готовности к проду, но интересно, почему решили не использовать Native Image в GraalVM. Решение с js в любом случае оригинальное и может быть из него что-то вырастет.

Сам концепт: https://gist.github.com/djspiewak/37a4ea0d7a5237144ec8b56a76ed080d
Прототип библиотеки: https://github.com/typelevel/feral


Новая микро-драма в Scala-сообществе: Роб Норрис, мейнтенер библиотеки для работы с SQL doobie, удалил интеграцию с другой библиотекой для SQL quill, потому что та вошла под крыло компании Ziverge, с которой у Норриса личный конфликт.

Поддерживать интеграции нескольких независимых библиотек в общем случае большая нагрузка на мейнтейнеров из-за необходимости «дружить» API, изменяющиеся без согласования друг с другом. То, что такая интеграция находилась непосредственно в doobie, а не поддерживалась в виде опосредованной библиотеки, накладывало определённую нагрузку и риски на мейнтейнеров. Поэтому, на мой взгляд, они в праве отказываться от поддержки таких решений по уйме причин: расхождения API, сложность работы с зависимостями, нехватка личного времени и т.д.

Но Норрис решил в один момент разбомбить Воронеж, то есть конечных пользователей, использующих эту интеграцию в проде. Что можно было сделать лучше?
1. Вынести интеграцию в отдельный репозиторий на гитхабе, сохранив неймспейс и название джарника. Если не хочется делать это самостоятельно, поставить issue на авторов quill с обозначенными сроками отказа от поддержки.
2. Задепрекейтить код интеграции, обозначив версию, в которой он будет удалён.
3. Изначально держать коннекторы в отдельных репозиториях и версионировать их независимо от основной библиотеки. В этом плане ZIO более дальновидно.

Собственно, сообщество подсуетилось и спасло эти аж ~200 строк кода интеграции. Которые, впрочем, можно и в свой проект скопипастить.

К слову, ценность этой интеграции для меня под вопросом. Писать код на DSL квилла и запускать на транзакторе из doobie не особо здорово, потому что doobie гвоздями прибит к jdbc. Хотелось бы наоборот, писать plain SQL в духе doobie и запускать их на асинхронном драйвере к постгре или другой базе, которые как раз поддерживаются в quill.

Эмоциональный выпад Норриса был замечен в высоких кабинетах Лозанны, и профессор Одерски лично отметил поведение деструктивным. В ответ на форуме контрибуторов Scala была созданна тема, в которой сообщество было обвинено в токсичности к меньшинствам, а Одерски чуть ли не в причастности к убийствам. Но об SQL и поддержке библиотек там ни слова, поэтому читать стоит только если очень хочется насладиться дивным новым миром.


Адам Фрейзер (кор-контрибутор ZIO) сделал доклад с очередным набросом на скалу-как-хаскель, дескать, теорию категорий надо знать. Мне понятно, почему Ziverge атакует «академическое» ФП с точки зрения маркетинга своей экосистемы. В этом ключе разумно давить на неофитов стереотипом о сложной математике, без которой в библиотеках конкурента не разобраться.

Но только на практике я программировал в функциональном стиле на Scala до появления ZIO, не зная при этом теорию категорий. Не горжусь своим невежеством в этом вопросе, но вэлью делать оно не мешает. Для программиста все эти монады, стрелки Клейсли и контравариантные функторы в коде быстро превращаются в паттерны со странными названиями. И их изучение мало отличается от изучения синглтонов, визиторов и абстрактных фабрик.

Tagless Final на скале мне неудобен скорее из-за упора на ад-хок полиморфизм, который заставляет учить слишком много тайпклассов. Правда для этого нужна хорошая память, а не математический аппарат :)


Совсем недавно скала-сообщество бурлило из-за внезапно обнаруженного отсутствия forward compatibility в Scala 3. Штука и правда неприятная, но, на мой взгляд, скалисты излишне всё драматизировали. На то они и скалисты!

Подробно о проблеме можно почитать тут.

А пару часов назад на форуме разработчиков языка появился план по исправлению этого неудобства — https://contributors.scala-lang.org/t/improving-scala-3-forward-compatibility/5298

TL;DR: будет `-target`, как в джаве.


В DI-фреймворке под названием ZIO можно автовайрить зависимости с помощью zio-magic, который ещё и встроят в версию 2.0. Код выглядит примерно так:

ZLayer.fromMagic[Env](
UsersRepository.live,
ClientsRepository.live,
RegistrationService.live
)

А дальше макрос на этапе компиляции выстроит граф зависимостей по входящим типам слоёв. Но частенько в приложении бывают классы, принимающие зависимости одного и того же типа. Допустим, grpc-клиенты к микросервисам BillingClient и NotificatorClient принимают в зависимости GrpcConfig. Содержимое этих конфигов очевидно разное, но на уровне типов этого не видно, поэтому нельзя понять, какой экземляр конфига к кому относится. Можно решить это ручным вайрингом:

ZLayer.fromMagic[Env](
Grpc.live,
Config.loadLayer[GrpcConfig]("billing") ++ ZLayer.environment[Has[Grpc]] >>> BillingClient.live,
Config.loadLayer[GrpcConfig]("notificator") ++ ZLayer.environment[Has[Grpc]] >>> NotificatorClient.live
)

Но мне это не нравится, потому что приходится внедрять куски ручного построения графа зависимостей в плоский список слоёв.

Так как вайринг происходит во время компиляции, различать зависимости нужно на уровне типов. Сразу пришла идея использовать тайп-теги: пометить слой зависимости каким-то тегом, и такой же тип затребовать в слое-получателе. Собственно, после некоторого пердолинга с компилятором было написано такое вот поделие: https://gist.github.com/poslegm/f252994a15453457e64d6498249928f3.

И с ним можно писать уже так:

ZLayer.fromMagic[Env](
Grpc.live,
Config.loadLayer[GrpcConfig]("billing").tagged[BillingClient.Service],
Config.loadLayer[GrpcConfig]("notificator").tagged[NotificatorClient.Service],
BillingClient.live.requireTagged[GrpcConfig, BillingClient.Service, Has[Grpc]],
NotificatorClient.live.requireTagged[GrpcConfig, NotificatorClient.Service, Has[Grpc]]
)

К сожалению не получилось придумать реализацию requireTagged, в которой не надо явно указывать компилятору тип "остатка" требуемых слоёв, кроме протегированного.

Зато теперь все зависимости укладываются в один плоский список, а zio-magic ругнётся, если не будет протегированной зависимости к какому-то слою.


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

Для меня стало открытием, что версионирование библиотек в скала-экосистеме — это вовсе не кривой semver, а другие схемы со своей формализацией. Так, стандартная библиотека версионируется по PVP: epoch.major.minor. В отличие от semver в нём не надо увеличивать первую цифру при каждом ломающем изменении: её можно инкрементить на усмотрение автора о важности обновления.

А другие библиотеки, например, проекты Typelevel, используют early-semver. В нём первая цифра увеличивается только при изменениях, ломающих бинарную совместимость. Если же major=0, то бинарную совместимость можно ломать при каждом минорном релизе. Собственно поэтому версия circe до сих пор начинается с нуля :) И рекомендацией для библиотек является как раз использование early-semver.




Длинный блогпост с подробным объяснением тайп констрейнтов в скале. От паттернов использования до собственной реализации.

Как работают эти магические


Посмотрел на Ruby Russia кейноут создателя языка. Там он всерёз говорит об оптимизации языка под микробенчмарки, потому что программисты слишком серьёзно к ним относятся, и выбирают языки, которые обгоняют Ruby. То есть буквально не из-за того, что ускорение рантайма принесёт какую-то ценность, а ради увеличения привлекательности языка. Дальше рассказывал про целый роадмап повышения перфоманса Ruby: многоуровневый JIT, гранты контрибуторам за микрооптимизации. И всё это под соусом того, что «медлительность» вредит маркетингу.

И вот не понимаю: программисты действительно столько внимания уделяют синтетическим бенчмаркам? Почему?

Конечно, есть критичные к перфомансу области разработки, но они уже заняты специфичными языками: Rust, C++. На Ruby пишутся в основном веб-бэкенды, а в них, чтобы упереться в производительность *языка*, надо особо постараться. Если в сервисе что-то тормозит, то почти всегда накосячил программист, а не рантайм. На своей практике припоминаю только два случая, когда тормоза на высоких нагрузках можно было с натяжкой списать на проблемы платформы. Оба связаны с (де-)сериализацией, что характерно :). Но и они решились не переписыванием со скалы на раст, а просто заменой библиотеки и небольшими оптимизациями кода.

А что до синтетических бенчмарков: в самом популярном сравнении HTTP серверов akka-http стабильно болтается около 190-го места. При этом в проде работает нормально и кушать не просит. Если что-то и тормозит, то в коде приложения, а не фреймворка. Современное состояние индустрии таково, что фреймворк со дна бенчмарков может держать внушительную нагрузку! И это не говоря о дешёвом железе.

Поэтому в моих глазах топ причин плохого перфоманса выглядит так:

1. баг в коде;
2. ошибка конфигурации;
3. ошибка дизайна системы;
4. ... программист обосрался где-то ещё ...
5. медленный рантайм языка.

Или в руби всё настолько плохо с производительностью, что даже скала по сравнению с ней летает? Или программистам комфортно списывать свои оплошности на «медленный язык»?


Канал перевалил за 1000 подписчиков, очень радостное для меня число! Пока вы не успели отписаться, сделаю стандартный телеграм-пост с каналами об ИТ, которые я читаю.

* @oleg_log, @oleg_fov — Олег пишет об индустрии. Удивляюсь его продуктивности и читаю, чтобы держать руку на пульсе;
* @bigflatmappa — канал контрибутора ФП-библиотек с историями о том, что он туда контрибутит. Стоит подписаться, чтобы проникнуться духом 10х программирования;
* @yourcybergrandpa — дед ворчит на облака;
* @architect_says — дед ворчит на Agile;
* @nikitonsky_pub — Никита Прокопов ворчит на всё вокруг;
* @nosingularity — о базах данных и инструментарии для них;
* @dereference_pointer_there — личный блог без чётко очерченной тематики (но частенько про Rust);
* @pmdaily — о продуктовой разработке и взаимоотношениях программиста с бизнесом;
* @scala_channel_ru — важные новости и анонсы из мира Scala;
* @daily_ponv — в основном ссылки на сложные пейперы;
* @shark_in_it — резюме пейперов о распределённых системах и базах данных;
* @scalabin — Антон давно ничего не писал, но если вдруг напишет — точно будет интересно;
* @consensus_io — о распределённых системах.

И конечно же чаты самого дружелюбного в мире сообщества, в котором высококвалифицированные специалисты помогают всем желающим стать 10x скалистом:

@scala_ru, @scala_learn, @scala_jobs, @ru_zio, @akka_ru


Удобнейший плагин для релизов артефактов в Maven Central sbt-ci-release переехал в организацию sbt на гитхабе и похоже стал официально рекомендованным способом автоматизации релизов.

Это здорово, потому что релизить что-то в Maven Central — долгий и болезненный процесс, а с этим плагином всё как-то само происходит.

20 last posts shown.