Рекурсивные типы. Часть 6. Пересвёртка на практике

В этот раз мы разберём, как пересвёртка рекурсивных структур данных помогает в решении задач динамического программирования.

Мультипарадигмальный язык программирования

В этот раз мы разберём, как пересвёртка рекурсивных структур данных помогает в решении задач динамического программирования.

Привет, Хабр! Меня зовут Артём Корсаков. Я пишу на Scala и руковожу группой разработчиков в компании «Криптонит», а также веду Scalabook — русскоязычную базу знаний по Scala и функциональному программированию. В этой статье расскажу про обработку ошибок в библиотеке http4s на Scala 3. Мы разберём, как настроить декодирование запросов так, чтобы клиент получал не просто код “500” или “422” с общим сообщением, а сразу видел развёрнутый список всех проблем в запросе. Например, что логин уже занят, пароль содержит недопустимые символы, а капча не введена.
Пожалуй, самая раздражающая ошибка — это получение кода “500” в ответ на запрос, который ты десять раз перепроверил, сверился с документацией и уверен на все 100%, что запрос рабочий. Даже на 110%!
В такие моменты раздражённо думаешь: “Что же этому серверу надо? Я же чётко сформулировал запрос!
Ответить на этот вопрос порой сложно. Например, я хочу зарегистрироваться на сайте, ввожу логин/пароль и получаю сообщение "Internal Server Error". Первое желание – тут же покинуть сайт и поискать более дружелюбный.
Давайте подумаем, как можно сделать сообщение об ошибке более информативным. Для этого будем использовать Scala 3, уточняющие типы и http4s.
Представим, что мы создаём API сервиса авторизации, который (помимо прочего) должен регистрировать новых пользователей.
Для начала определим структуру данных для создания нового пользователя.
Мотивацией для написания этого поста стали два года собеседований JS/TS-инженеров. Я интересуюсь языками и функциональным программированием, поэтому всегда «разбавлял» технические вопросы разговором о парадигмах. И заметил любопытную асимметрию.
Об ООП кандидаты рассуждали уверенно — но в основном на концептуальном уровне, не вдаваясь в то, как именно ООП реализовано в JavaScript. С FP картина была другой: уверенности меньше, зато критика — конкретная и повторяющаяся: «иммутабельность дорогая по памяти», «рекурсия небезопасна из-за стека». Что характерно — эти аргументы почти всегда были сформулированы через опыт работы с JS, а не с Haskell, Clojure или Scala.
Это важная деталь. Любая парадигма существует на двух уровнях: концептуальном (идеальная модель) и имплементационном (как конкретный язык эту модель выражает). Судить о FP по JS — примерно то же самое, что судить об ООП по bash-скриптам с глобальными переменными.
Параллельно я регулярно слышал, что JS — функциональный язык. Аргументы варьировались от «там есть .map()» до рассуждений о чистых функциях и каррировании. Именно это и стало поводом для поста: я хочу объяснить, что я считаю функциональным языком — и почему JS таковым не является. Не перечислить отсутствующие фичи, а показать, почему их нет и что это значит в реальном рантайме.

В поиске способов построения монад мы уже рассматривали сопряжения и расширения функторов. Свойства обеих абстракций выражаются естественными преобразованиями, но ещё не был представлен универсальный метод их получения. В этот раз мы познакомимся с техникой, позволяющей решать такие задачи.
В предыдущих частях мы видели, что все манипуляции в категориях происходят между морфизмами и совокупностями морфизмов. Для теории типов это вообще очевидный факт — любые конструкции (типы) являются функциями, преимущественно высокого порядка. В контексте вычислений обе теории представляют собой различные точки зрения на этот процесс, и между ними есть немало общего. В частности, функторы являются в первую очередь функциями на морфизмах, из которого следует (нефункциональное в общем случае) сопоставление объектов. Поэтому ожидается, что должен существовать конструктивный вывод возможностей описанных ранее абстракций.
Давайте рассмотрим технику исчисления концов через призму теории типов с использованием языка программирования Scala.

Эту печальную историю стоит прочесть всем, кто еще не понял разницы между «создать» и «владеть» применительно к программному обеспечению.
Заодно узнаете, чем на самом деле занимаются большинство разработчиков на крупных проектах.

Строгая типизация не всегда спасает от глупых ошибок. Если userId, orderId и productId — это один и тот же Int или Long, компилятор не увидит разницы и спокойно пропустит неверный аргумент. В Scala 3 для таких случаев есть opaque types: они позволяют сделать доменные типы различимыми на этапе компиляции, но без лишних обёрток и накладных расходов в рантайме. Разберём, как это работает и чем этот подход лучше type alias, case class и AnyVal.

Рефакторинг — это не уборка, это хирургия на живом коде. Большинство провалов здесь не технические: смешали рефакторинг с улучшениями, сделали один огромный коммит, затянули релиз — и три недели работы ушли в мусор. Собрал 10 ошибок из реальных проектов: с примерами кода, разбором механики и способами не наступить на те же грабли.

Как перевести продакшен-проект на рельсы agent-driven development - когда LLM-агенты становятся полноценными участниками разработки, а не просто подсказчиками в автокомплите ? Реальный опыт на реальном проекте !
Продолжаем улучшать Feedback Loop. В предыдущей статье я ускорил прогон тестов в 6 раз. Теперь — следующий шаг: LLM-агент генерирует тесты. Два подхода (sprint-driven и coverage-driven), шестиуровневый pipeline верификации, двух-агентная архитектура, оптимизация feedback loop — и 68 тестовых файлов на выходе с acceptance rate 86.8% при ревью живыми разработчиками.
В статье — конкретика: как анализировал покрытие и свежесть документации, как ускорял компиляцию для агента, на чём экономил токены, и что сказала команда на code review.

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

Всем привет! Меня зовут Сергей и последнее время я занимаюсь backend-разработкой на Scala. Вообще, мой опыт асинхронного программирования на Scala и C# составляет более десяти лет, и за это время сложилось вполне достаточное понимание этой темы. Во всяком случае, тогда мне так казалось…
Но недавно в беседе с коллегами обнаружились большие проблемы в моём «понимании», что мотивировало детально разобраться в этом вопросе.

Почти 800 тестов, 10 минут на прогон, каждый пуш — ожидание на CI. Знакомо? Рассказываю, как довёл время до 101 секунды: снижение таймаутов, параллелизм ScalaTest, shared Testcontainers и защита от регрессий. Scala, SBT, PostgreSQL, GraalVM — конкретные шаги и подводные камни.

Если вы пишете тесты на ZIO, то с моками, скорее всего, уже сталкивались. И почти наверняка — с ZIO Mock. Формально он решает задачу, но на практике ломает Arrange‑Act‑Assert, «краснит» в IDEA и иногда падает так, что вы видите только InvalidCallException: null. В Яндекс Вертикалях мы довольно долго жили с этой библиотекой — пока количество таких тестов не перевалило за пару сотен и они не расползлись по десятку команд.
Меня зовут Женя Веретенников, я тимлид в Яндекс Вертикалях и последние годы занимаюсь инструментами для Scala‑разработчиков и инфраструктурой бэкенд‑монорепозитория. Когда стало ясно, что ZIO Mock больше мешает, чем помогает, мы решили отказаться от него полностью — и подружить ZIO Test с классическим ScalaMock. Он даёт более предсказуемый синтаксис и понятные ошибки, но из коробки с ZIO не работает.
В этой статье я расскажу не о том, как пользоваться новой библиотекой, а о том, как мы её делали: какие ограничения ZIO‑стека пришлось учитывать, где пришлось лезть под капот ScalaMock и ZIO Test и во что в итоге превратилась эта инженерная затея. Это история про построение собственного test tooling в большой Scala‑кодовой базе — с честными компромиссами и практическими выводами.

"Loom" означает "ткацкий станок" - так назывался проект по добавлению асинхронности в джаву. Тяжёлые системные потоки заменили легковесными виртуальными потоками. Потоки и нити в английском называются одинаково - thread - отсюда название.
Проект успешно внедрили пару лет назад, и я, к сожалению, это полностью пропустил. Основная причина, конечно - что я в принципе не ожидал от джавы никаких прорывов (и оказался на 100% неправ!) Ну и, как бэкенд-разработчик на питоне, я не очень-то слежу за развитием JVM-языков.
Но исправляюсь: Project Loom - это блестящая идея. Учитывая, что его внедрили совместимым образом, и почти не было ломающих изменений. Я считаю, это оптимальная реализация асинхронности для высокоуровневого серверного языка.
Уже почти все использующие JVM языки переехали на новую версию JVM, и, таким образом, на проект Loom: деваться им некуда. Так что, есть надежда, что это изменение откроет новую главу для некоторых из них. Есть также предположение, как оно может повлиять на скриптовые языки, такие как питон. Гипотеза только.
Также, вы узнаете, почему Гвидо ван Россум не ошибся, когда добавлял async/await в питон, а Мацумото — создатель Ruby — тоже угадал, когда, наоборот, отказался от async/await.

Привет! Меня зовут Артём. Я Scala Tech Lead в компании “Криптонит” и автор Scalabook — русскоязычной базы знаний по Scala и функциональному программированию. В прошлой статье я разбирал можно ли программировать без циклов. Сегодня хотелось бы подлить масла в огонь и продолжить разбирать альтернативы императивным циклам в мире функционального программирования.
Данная статья посвящена свёрткам (folds) и развёрткам (unfolds). Это модели вычислений, работающие поверх рекурсивных типов данных, таких как связанные списки, деревья и т.д. Свёртки и развёртки образуют мощную пару абстракций: если свёртки предназначены для потребления рекурсивных структур данных, то развёртки ответственны за генерацию структур данных из некоторого начального состояния.
Допустим у нас есть связанный список:

Это вторая (и заключительная) часть цикла статей о нашей миграции с Zeppelin. О причинах и первом опыте перехода с Zeppelin я рассказал здесь. В данной статье я хочу большее внимание уделить второму виду Zeppelin notebook, которые срочно нуждались в переносе.
Конечно, отчеты для клиентов не были настолько "забагованы" как рассылки: большая часть проблем с Zeppelin крылась именно в cron-е, который временами работал как хотел (или в интерпретаторах, мы так и не смогли разобраться, но ошибка интерпретатора возникала только когда запускали через cron). В отчетах этого звена не было, поэтому их перенос был плавным и основан скорее на особенностях UI/UX дизайна.
Данная статья может быть полезна аналитикам, которые не знают, какой инструмент использовать для своих задач и думают, что писать графический интерфейс крайне сложно (спойлер, нет), а также для команд, которые устали от Zeppelin как UI-инструмента (и от Zeppelin в целом)

В этой статье я хотел бы обобщить всё, что мы знаем про Hot/Live Reloading на JVM, и далее показать, как я пришел к реализации универсального Live Reloading решения для любых веб-приложений на JVM.
Вкратце, в этой статье мы:
🔹 Попробуем сформулировать, какие виды релоадинга бывают.
🔹 Подробно рассмотрим, какие реализации существуют на JVM.
🔹 И немножко обсудим детали реализации универсального решения, и что вообще к нему привело.