<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title></title>
    <description></description>
    <link>https://chugunkov.dev//</link>
    <atom:link href="https://chugunkov.dev//feed.xml" rel="self" type="application/rss+xml"/>
    <pubDate>Fri, 27 Mar 2026 22:17:26 +0100</pubDate>
    <lastBuildDate>Fri, 27 Mar 2026 22:17:26 +0100</lastBuildDate>
    <generator>Jekyll v4.3.2</generator>
    
      <item>
        <title>Applicative for</title>
        <description>&lt;p&gt;Kit Langton, автор &lt;a href=&quot;https://github.com/kitlangton/zio-magic&quot;&gt;макроса для автовайринга
ZLayer&lt;/a&gt;, отметился в любопытном
&lt;a href=&quot;https://contributors.scala-lang.org/t/for-syntax-for-parallel-computations-afor-applicative-for-comprehension/4474/1&quot;&gt;пропозале&lt;/a&gt;
на форуме разработчиков компилятора Scala. В пропозале обсуждается
синтаксический сахар для аппликативной композиции по аналогии с
&lt;code&gt;for-comprehension&lt;/code&gt; для монадок. Если сильно упрощать смысл аппликативных
вычислений до наиболее частого практического применения – это вычисление
нескольких эффектов параллельно и сбор результатов в одном месте (академики, не
ругайтесь).&lt;/p&gt;

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

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span class=&quot;nv&quot;&gt;ZIO&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;mapParN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fetchName&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetchAge&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

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

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span class=&quot;n&quot;&gt;afor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetchName&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetchAge&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;В предложенном варианте меньше бросается в глаза, что вычисления побегут в
параллель, но читается он гораздо лучше. В Haskell такое &lt;a href=&quot;https://gitlab.haskell.org/ghc/ghc/-/wikis/applicative-do&quot;&gt;расширение
компилятора&lt;/a&gt; уже
реализовано и выглядит довольно симпатично.&lt;/p&gt;

&lt;p&gt;Так вот, Kit написал &lt;a href=&quot;https://github.com/kitlangton/parallel-for/&quot;&gt;работающий PoC
макроса&lt;/a&gt;, который даёт такой
синтаксис без доработок компилятора. Пока только для ZIO.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span class=&quot;n&quot;&gt;par&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetchName&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fetchAge&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;yield&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;age&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Макрос строит граф вычислений из обёрнутого &lt;code&gt;for-comprehension&lt;/code&gt;, топологически
его сортирует и параллелит всё, что можно. Утилита уже сейчас выглядит
привлекательно, и может быть её втащат в ZIO, как это уже было с zio-magic.&lt;/p&gt;

&lt;p&gt;https://github.com/kitlangton/parallel-for&lt;/p&gt;
</description>
        <pubDate>Sun, 06 Feb 2022 01:00:00 +0100</pubDate>
        <link>https://chugunkov.dev//2022/02/06/applicative-for.html</link>
        <guid isPermaLink="true">https://chugunkov.dev//2022/02/06/applicative-for.html</guid>
        
        
      </item>
    
      <item>
        <title>Uber DOMA / Layers Design</title>
        <description>&lt;p&gt;Uber публично задокументировал свой подход к организации микросервисов —
&lt;a href=&quot;https://eng.uber.com/microservice-architecture/&quot;&gt;Domain-Oriented Microservice
Architecture&lt;/a&gt; (DOMA). Он
решает проблему колоссального возрастания сложности системы у организаций с
несколькими тысячями микросервисов. Для организаций меньшего масштаба весь
подход будет избыточен, а вот идею «слоёного дизайна» стоит почерпнуть.&lt;/p&gt;

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

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

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

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

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

&lt;p&gt;Важно не использование «слоёв» в каноничном уберовском понимании, а наличие
иерархии в организации системы. Без неё сложность растёт неконтролируемо:
домены переплетаются, экспертиза размазывается между командами, граф
зависимостей сервисов превращается в ёжика. Выделить правильную иерархию без
лишних абстракций — сложная архитектурная задача, но даже несовершенная
иерархия лучше её отсутствия.&lt;/p&gt;
</description>
        <pubDate>Wed, 26 Jan 2022 01:00:00 +0100</pubDate>
        <link>https://chugunkov.dev//2022/01/26/uber-doma-layers.html</link>
        <guid isPermaLink="true">https://chugunkov.dev//2022/01/26/uber-doma-layers.html</guid>
        
        
      </item>
    
      <item>
        <title>C4 Model</title>
        <description>&lt;p&gt;Меня сильно заинтересовали способы грамотной визуализации архитектуры
программных систем, потому что диаграмы уровня &lt;code&gt;[kafka] → [scala] →
[postgresql]&lt;/code&gt; порядком поднадоели. Хочется выработать максимально
информативный, но в тоже время удобный способ рисовать то, что мы проектируем.&lt;/p&gt;

&lt;p&gt;Из того, что нашёл, больше всего понравился фреймворк &lt;a href=&quot;https://c4model.com/&quot;&gt;C4
model&lt;/a&gt;. Но зачем вообще нужен целый фреймворк для
визуализации? Без него каждый раз приходится импровизировать, что нарисовать на
диаграмме. А нарисовать хочется многое: юзер-стори, технологии, карту
микросервисов и хранилищ, технические ограничения… В итоге получается либо
картинка с хаотичным набором стрелочек и квадратиков с перемешанными
абстракциями “всё в одном”, либо фрустрация и рисование примитивной схемы а-ля
“ну тут сервис, тут база, всё понятно вроде”.&lt;/p&gt;

&lt;p&gt;C4 предлагает решение через разделение уровней детализации: нарисовать
несколько схем от взляда на систему с высоты птичьего полёта до всё более
подробных представлений отдельных процессов. Описание слоёв есть на
&lt;a href=&quot;https://c4model.com/&quot;&gt;сайте&lt;/a&gt;, я приведу свою краткую интерпретацию:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Контекст&lt;/strong&gt;. Верхнеуровневый взгляд на всю систему в целом, и то, как она
взаимодействует с другими целостными программными продуктами (например,
сторонние CRM или решения для эквайринга). На этом уровне обязательно
фигурирует &lt;em&gt;пользователь&lt;/em&gt; и его основная цель взаимодействия с программой.
Диаграма должна выглядеть настолько просто, чтобы её можно было показать
нетехническим специалистам.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Контейнеры&lt;/strong&gt;. Контейнер — это изолированная единица рантайма: серверное
или клиентское приложение, база данных, очередь сообщений. Здесь нужно
погрузиться внутрь системы и крупноблочно отобразить её ключевые
составляющие. Какие есть клиентские приложения, как они взаимодействуют с
сервером, в каких базах лежат самые ценные данные. Можно выделить несколько
групп контейнеров по сценариям использования, если их слишком много для
одной диаграммы.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Компоненты&lt;/strong&gt;. Компонент — это уже логическая единица внутри контейнера.
Здесь контейнер декомпозируется на набор функциональных единиц с описанием
их ответственности и связности друг с другом. Например, если контейнер —
сервис с учётными записями пользователей, то в качестве компонент можно
отобразить модули аутентификации, управления правами, регистрации, поиска и
редактирования.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Код&lt;/strong&gt;. Тут можно нарисовать классический UML со связями внутри кода.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Мне нравится подход, но с парой ремарок:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Я бы сократил C4 до C3. Визуализация связей внутри кода приложения кажется
избыточной: если все сервисы пишутся по единым паттернам в неком подобии
гексагональной архитектуры, то и связи между модулями будут типовыми. Лучше
инвестировать в выработку организационных стандартов кодирования, чем в
рисование UML.&lt;/li&gt;
  &lt;li&gt;В микросервисных реалиях одно приложение содержит в себе совсем немного
компонент, поэтому на третьем уровне вместо статической диаграммы
внутренностей отдельного приложения лучше нарисовать основные процессы,
которые оно выполняет. Для этого хорошо подходит &lt;em&gt;&lt;a href=&quot;https://ru.wikipedia.org/wiki/%D0%94%D0%B8%D0%B0%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B0_%D0%BF%D0%BE%D1%81%D0%BB%D0%B5%D0%B4%D0%BE%D0%B2%D0%B0%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D0%B8&quot;&gt;диаграмма
последовательности&lt;/a&gt;&lt;/em&gt;
из PlantUML.&lt;/li&gt;
  &lt;li&gt;Описание слоёв не является догмой и должно адаптироваться под нужды
организации или команды.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Важным следствием разделения визуализации на уровни является разная скорость
устаревания диаграмм. Диаграмма первого уровня может жить годами, а на низких
уровнях имеет смысл рисовать самые сложные и важные процессы. Это снизит
расходы на актуализацию и боль от устаревания.&lt;/p&gt;
</description>
        <pubDate>Fri, 21 Jan 2022 01:00:00 +0100</pubDate>
        <link>https://chugunkov.dev//2022/01/21/c4-model.html</link>
        <guid isPermaLink="true">https://chugunkov.dev//2022/01/21/c4-model.html</guid>
        
        
      </item>
    
      <item>
        <title>Драма у doobie и quill</title>
        <description>&lt;p&gt;Новая микро-драма в Scala-сообществе: Роб Норрис, мейнтенер библиотеки для
работы с SQL doobie, &lt;a href=&quot;https://github.com/tpolecat/doobie/pull/1587&quot;&gt;удалил&lt;/a&gt;
интеграцию с другой библиотекой для SQL quill, потому что та вошла под крыло
компании Ziverge, с которой у Норриса личный конфликт.&lt;/p&gt;

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

&lt;p&gt;Но Норрис решил в один момент разбомбить Воронеж, то есть конечных
пользователей, использующих эту интеграцию в проде. Что можно было сделать
лучше?&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Вынести интеграцию в отдельный репозиторий на гитхабе, сохранив неймспейс и
название джарника. Если не хочется делать это самостоятельно, поставить
issue на авторов quill с обозначенными сроками отказа от поддержки.&lt;/li&gt;
  &lt;li&gt;Задепрекейтить код интеграции, обозначив версию, в которой он будет удалён.&lt;/li&gt;
  &lt;li&gt;Изначально держать коннекторы в отдельных репозиториях и &lt;a href=&quot;https://chugunkov.dev/2019/05/01/satellites.html&quot;&gt;версионировать их
независимо&lt;/a&gt; от основной
библиотеки. В этом плане ZIO более
&lt;a href=&quot;https://github.com/zio/?q=interop&amp;amp;type=&amp;amp;language=&amp;amp;sort=&quot;&gt;дальновидно&lt;/a&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Собственно, сообщество подсуетилось и
&lt;a href=&quot;https://github.com/polyvariant/doobie-quill&quot;&gt;спасло&lt;/a&gt; эти аж ~200 строк кода
интеграции. Которые, впрочем, можно и в свой проект скопипастить.&lt;/p&gt;

&lt;p&gt;К слову, ценность этой интеграции для меня под вопросом. Писать код на DSL
квилла и запускать на транзакторе из doobie не особо здорово, потому что doobie
гвоздями прибит к jdbc. Хотелось бы наоборот, писать plain SQL в духе doobie и
запускать их на асинхронном драйвере к постгре или другой базе, которые как раз
&lt;a href=&quot;https://getquill.io/#contexts-quill-jasync&quot;&gt;поддерживаются&lt;/a&gt; в quill.&lt;/p&gt;

&lt;p&gt;Эмоциональный выпад Норриса был замечен в высоких кабинетах Лозанны, и
профессор Одерски лично
&lt;a href=&quot;https://github.com/tpolecat/doobie/pull/1587#issuecomment-961415475&quot;&gt;отметил&lt;/a&gt;
поведение деструктивным. В ответ на форуме контрибуторов Scala была созданна
&lt;a href=&quot;https://contributors.scala-lang.org/t/politics-safety-and-the-future-of-scala/5317&quot;&gt;тема&lt;/a&gt;,
в которой сообщество было обвинено в токсичности к меньшинствам, а Одерски чуть
ли не в причастности к убийствам. Но об SQL и поддержке библиотек там ни слова,
поэтому читать стоит только если хочется насладиться дивным новым миром.&lt;/p&gt;
</description>
        <pubDate>Fri, 05 Nov 2021 01:00:00 +0100</pubDate>
        <link>https://chugunkov.dev//2021/11/05/doobie-quill-drama.html</link>
        <guid isPermaLink="true">https://chugunkov.dev//2021/11/05/doobie-quill-drama.html</guid>
        
        
      </item>
    
      <item>
        <title>Нужен ли теоркат скалисту? (нет)</title>
        <description>&lt;p&gt;Адам Фрейзер (кор-контрибутор ZIO) сделал
&lt;a href=&quot;https://github.com/adamgfraser/the-next-generation-of-scala/blob/master/the-next-generation-of-scala.pdf&quot;&gt;доклад&lt;/a&gt;
с очередным набросом на скалу-как-хаскель, дескать, теорию категорий надо
знать. Мне понятно, почему Ziverge атакует «академическое» ФП с точки зрения
маркетинга своей &lt;a href=&quot;https://ziverge.com/openSource/&quot;&gt;экосистемы&lt;/a&gt;. В этом ключе
разумно давить на неофитов стереотипом о сложной математике, без которой в
библиотеках конкурента не разобраться.&lt;/p&gt;

&lt;p&gt;Но только на практике я программировал в функциональном стиле на Scala до
появления ZIO, не зная при этом теорию категорий. Не горжусь своим невежеством
в этом вопросе, но вэлью делать оно не мешает. Для программиста все эти
&lt;a href=&quot;http://typelevel.org/cats/typeclasses/monad.html&quot;&gt;монады&lt;/a&gt;, &lt;a href=&quot;http://typelevel.org/cats/datatypes/kleisli.html&quot;&gt;стрелки
Клейсли&lt;/a&gt; и &lt;a href=&quot;http://typelevel.org/cats/typeclasses/contravariant.html&quot;&gt;контравариантные
функторы&lt;/a&gt; в коде
быстро превращаются в паттерны со странными названиями. И их изучение мало
отличается от изучения синглтонов, визиторов и абстрактных фабрик.&lt;/p&gt;

&lt;p&gt;Tagless Final на скале мне неудобен скорее из-за упора на ад-хок полиморфизм,
который заставляет учить слишком много тайпклассов. Правда для этого нужна
хорошая память, а не математический аппарат :)&lt;/p&gt;
</description>
        <pubDate>Sun, 31 Oct 2021 02:00:00 +0200</pubDate>
        <link>https://chugunkov.dev//2021/10/31/fraser-category-theory.html</link>
        <guid isPermaLink="true">https://chugunkov.dev//2021/10/31/fraser-category-theory.html</guid>
        
        
      </item>
    
      <item>
        <title>Тайптеги для зависимостей в ZIO</title>
        <description>&lt;p&gt;В DI-фреймворке под названием ZIO можно автовайрить зависимости с помощью
расширения &lt;a href=&quot;https://github.com/kitlangton/zio-magic&quot;&gt;zio-magic&lt;/a&gt;, который ещё и
встроят в версию 2.0. Код выглядит примерно так:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span class=&quot;nv&quot;&gt;ZLayer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;fromMagic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;](&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;UsersRepository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;live&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;ClientsRepository&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;live&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;RegistrationService&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;live&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

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

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span class=&quot;nv&quot;&gt;ZLayer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;fromMagic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;](&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;Grpc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;live&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;Config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;loadLayer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;GrpcConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;billing&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ZLayer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Has&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Grpc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;BillingClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;live&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;Config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;loadLayer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;GrpcConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;notificator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;++&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;ZLayer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;environment&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Has&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Grpc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;NotificatorClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;live&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

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

&lt;p&gt;Так как вайринг происходит во время компиляции, различать зависимости нужно на
уровне типов. Сразу пришла идея использовать
&lt;a href=&quot;https://medium.com/iterators/to-tag-a-type-88dc344bb66c&quot;&gt;тайп-теги&lt;/a&gt;: пометить
слой зависимости каким-то тегом, и такой же тип затребовать в слое-получателе.
Собственно, после некоторого пердолинга с компилятором было написано такое вот
поделие:
&lt;a href=&quot;https://gist.github.com/poslegm/f252994a15453457e64d6498249928f3&quot;&gt;https://gist.github.com/poslegm/f252994a15453457e64d6498249928f3&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;И с ним можно писать уже так:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span class=&quot;nv&quot;&gt;ZLayer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;fromMagic&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Env&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;](&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;Grpc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;live&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;Config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;loadLayer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;GrpcConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;billing&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;tagged&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;BillingClient.Service&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;Config&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;loadLayer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;GrpcConfig&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;notificator&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;tagged&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;NotificatorClient.Service&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;],&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;BillingClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;live&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;requireTagged&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;GrpcConfig&lt;/span&gt;, &lt;span class=&quot;kt&quot;&gt;BillingClient.Service&lt;/span&gt;, &lt;span class=&quot;kt&quot;&gt;Has&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Grpc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]],&lt;/span&gt;
  &lt;span class=&quot;nv&quot;&gt;NotificatorClient&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;live&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;py&quot;&gt;requireTagged&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;GrpcConfig&lt;/span&gt;, &lt;span class=&quot;kt&quot;&gt;NotificatorClient.Service&lt;/span&gt;, &lt;span class=&quot;kt&quot;&gt;Has&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;Grpc&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]]&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;К сожалению не получилось придумать реализацию &lt;code&gt;requireTagged&lt;/code&gt;, в которой не
надо явно указывать компилятору тип “остатка” требуемых слоёв, кроме
протегированного. Зато теперь все зависимости укладываются в один плоский
список, а zio-magic ругнётся, если не будет протегированной зависимости к
какому-то слою.&lt;/p&gt;
</description>
        <pubDate>Tue, 19 Oct 2021 02:00:00 +0200</pubDate>
        <link>https://chugunkov.dev//2021/10/19/zlayer-tagged.html</link>
        <guid isPermaLink="true">https://chugunkov.dev//2021/10/19/zlayer-tagged.html</guid>
        
        
      </item>
    
      <item>
        <title>Версионирование библиотек в Scala</title>
        <description>&lt;p&gt;Ещё полгода назад в sbt появилась возможность прописывать схему версионирования
зависимостей. Это помогает системе сборке сигнализировать о бинарной
несовместимости зная правила версионирования конкретной библиотеки, то есть с
меньшим количеством ложных срабатываний. Подробнее об этом написано
&lt;a href=&quot;https://scala-lang.org/blog/2021/02/16/preventing-version-conflicts-with-versionscheme.html&quot;&gt;здесь&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Для меня стало открытием, что версионирование библиотек в скала-экосистеме —
это вовсе не кривой &lt;a href=&quot;https://semver.org/lang/ru/&quot;&gt;semver&lt;/a&gt;, а другие схемы со
своей формализацией. Так, стандартная библиотека версионируется по
&lt;a href=&quot;https://pvp.haskell.org/&quot;&gt;PVP&lt;/a&gt;: &lt;code&gt;epoch.major.minor&lt;/code&gt;. В отличие от semver в нём
не надо увеличивать первую цифру при каждом ломающем изменении: её можно
инкрементить на усмотрение автора о важности обновления.&lt;/p&gt;

&lt;p&gt;А другие библиотеки, например, проекты Typelevel, используют
&lt;a href=&quot;https://docs.scala-lang.org/overviews/core/binary-compatibility-for-library-authors.html#versioning-scheme---communicating-compatibility-breakages&quot;&gt;early-semver&lt;/a&gt;.
В нём первая цифра увеличивается только при изменениях, ломающих &lt;em&gt;бинарную&lt;/em&gt;
совместимость. Если же major=0, то бинарную совместимость можно ломать при
каждом минорном релизе. Собственно поэтому версия
&lt;a href=&quot;https://github.com/circe/circe&quot;&gt;circe&lt;/a&gt; до сих пор начинается с нуля :) И
рекомендацией для библиотек является как раз использование early-semver.&lt;/p&gt;
</description>
        <pubDate>Wed, 13 Oct 2021 02:00:00 +0200</pubDate>
        <link>https://chugunkov.dev//2021/10/13/scala-versioning.html</link>
        <guid isPermaLink="true">https://chugunkov.dev//2021/10/13/scala-versioning.html</guid>
        
        
      </item>
    
      <item>
        <title>Ссылка: объяснение тайп констрейнтов</title>
        <description>&lt;p&gt;Длинный блогпост с подробным объяснением тайп констрейнтов в скале. От
паттернов использования до собственной реализации.&lt;/p&gt;

&lt;p&gt;Как работают эти магические &lt;code&gt;&amp;lt;:&amp;lt;&lt;/code&gt;, &lt;code&gt;=:=&lt;/code&gt;, и почему недостаточно обычных тайп
баундов. Для меня при изучении скалы это долгое время был один из самых
непонятных вопросов.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://blog.bruchez.name/posts/generalized-type-constraints-in-scala/&quot;&gt;https://blog.bruchez.name/posts/generalized-type-constraints-in-scala/&lt;/a&gt;&lt;/p&gt;
</description>
        <pubDate>Sat, 09 Oct 2021 02:00:00 +0200</pubDate>
        <link>https://chugunkov.dev//2021/10/09/constraints-ref.html</link>
        <guid isPermaLink="true">https://chugunkov.dev//2021/10/09/constraints-ref.html</guid>
        
        
      </item>
    
      <item>
        <title>О перфомансе</title>
        <description>&lt;p&gt;Посмотрел на Ruby Russia кейноут создателя языка. Там он всерёз говорит об
оптимизации языка под микробенчмарки, потому что программисты слишком серьёзно
к ним относятся, и выбирают языки, которые обгоняют Ruby. То есть буквально не
из-за того, что ускорение рантайма принесёт какую-то ценность, а ради
увеличения привлекательности языка. Дальше рассказывал про целый роадмап
повышения перфоманса Ruby: многоуровневый JIT, гранты контрибуторам за
микрооптимизации. И всё это под соусом того, что “медленность” вредит
маркетингу.&lt;/p&gt;

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

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

&lt;p&gt;А что до синтетических бенчмарков: в самом популярном
&lt;a href=&quot;https://www.techempower.com/benchmarks/&quot;&gt;сравнении&lt;/a&gt; HTTP серверов akka-http
стабильно болтается около 190-го места. При этом в проде работает нормально и
кушать не просит. Если что-то и тормозит, то в коде приложения, а не
фреймворка. Современное состояние индустрии таково, что фреймворк со дна
бенчмарков может держать внушительную нагрузку! И это не говоря о дешёвом
железе.&lt;/p&gt;

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

&lt;ol&gt;
  &lt;li&gt;баг в коде;&lt;/li&gt;
  &lt;li&gt;ошибка конфигурации;&lt;/li&gt;
  &lt;li&gt;ошибка дизайна системы;&lt;/li&gt;
  &lt;li&gt;… программист обосрался где-то ещё …&lt;/li&gt;
  &lt;li&gt;медленный рантайм языка.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Или в руби всё настолько плохо с производительностью, что даже скала по
сравнению с ней летает? Или программистам комфортно списывать свои оплошности
на “медленный язык”?&lt;/p&gt;
</description>
        <pubDate>Tue, 05 Oct 2021 02:00:00 +0200</pubDate>
        <link>https://chugunkov.dev//2021/10/05/ruby-perfomance.html</link>
        <guid isPermaLink="true">https://chugunkov.dev//2021/10/05/ruby-perfomance.html</guid>
        
        
      </item>
    
      <item>
        <title>Страх и ужас в ФП на тайпскрипте</title>
        <description>&lt;p&gt;От чтения тайпскриптовых ФП-библиотек (&lt;a href=&quot;https://github.com/gcanti/fp-ts&quot;&gt;раз&lt;/a&gt;,
&lt;a href=&quot;https://github.com/effect-TS/core&quot;&gt;два&lt;/a&gt;) складывается впечатление, что они
проходят тот же этап, что и скалисты несколько лет назад, когда тащили всё
подряд из хаскеля. Только в тайпскрипте это выглядит как будто бы ещё хуже:
один только &lt;a href=&quot;https://gcanti.github.io/fp-ts/guides/do-notation.html&quot;&gt;заменитель
do-нотации&lt;/a&gt; чего стоит.
В исходников ещё много вещей, которые интересны своей изобретательностью в
обходе ограничений компилятора, но болезнены в использовании.&lt;/p&gt;

&lt;p&gt;Похоже, что “переводы” хаскеля — это проклятие всех языков с развитой системой
типов, а идиоматичный инструментарий для ФП появляется только спустя годы
экспериментов.&lt;/p&gt;

&lt;p&gt;Система типов в TS при этом передовая для промышленного языка: &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/conditional-types.html&quot;&gt;условные
типы&lt;/a&gt;,
типы-литералы даже с &lt;a href=&quot;https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html&quot;&gt;поддержкой
шаблонов&lt;/a&gt;,
flow typing, пересечения и объединения типов. Часть этих фич только появилась в
Scala 3, а в тайпскрипте существует уже несколько лет. Возможно, тайплевельные
истории там не летят из-за интеропа с JS, который гораздо более неприятный, чем
интероп с джавой в Scala-мире.&lt;/p&gt;
</description>
        <pubDate>Thu, 15 Jul 2021 02:00:00 +0200</pubDate>
        <link>https://chugunkov.dev//2021/07/15/ts-fp-pain.html</link>
        <guid isPermaLink="true">https://chugunkov.dev//2021/07/15/ts-fp-pain.html</guid>
        
        
      </item>
    
  </channel>
</rss>
